標籤:模板 c#委託 c++11 vector
這篇文章本來是前天發的,但是不知道為什麼CSDN上沒有顯示,可能是我沒發,記錯了。又由於沒有留底稿,還是重寫一下吧,也為知己不留底稿的惡習做個標記。
之所以接觸C++11是因為自己前天突發奇想想用C++來類比一下C#裡的委託,但是嘗試過很多方法和各種搜尋後,知道之前的C++是不支援模板重載的,所以不可能通過編寫多種版本的模板來實現變參的效果,如果使用《C++設計新思維》裡的TypeList方式的話,我不知道能不能做到,即使能做到也不可能達到預期的效果,並且TypeList是一個重型武器,構建起來比較費時(個人癖好,如果不是為了工程,我會自己構建一個的,否則可以用Loki庫裡面的)。繼續搜尋之後發現,C++11支援變參模板了!所以變參的問題就算解決了。
下一個問題是委託支援=、+=、-=操作符,所以為了讓類比出來的C#委託也支援這些功能,所以需要一個可自動擴充的容器,鏈表其實不錯,但是對於本文的目的,數組就夠了,所以我先做了一個簡單的容器Vector(作為學習沒有使用標準庫裡的vector):
#ifndef _VECTOR_H_#define _VECTOR_H_namespace MyDataStructure{ template<typename T> class Vector { private: int capacity; int size_; T *vals; private: void copy(const Vector& v) { if (v.capacity != 0) { vals = new T[v.capacity]; memcpy(vals, v.vals, v.capacity*sizeof(T)); } else { vals = NULL; } capacity = v.capacity; size_ = v.size_; } void destroy() { capacity = size_ = 0; if (vals) delete[]vals; vals = NULL; } void forward_move(int start, int end,int targ) { if (start > end) return; if (targ >= start) return; while ( start <= end) vals[targ++] = vals[start++]; } public: Vector() : capacity(0), size_(0), vals(NULL){}; Vector(int capacity) : capacity(capacity), size_(0) { vals = new T[capacity]; memset(vals, 0, capacity*sizeof(T)); } Vector(const Vector& v) { copy(v); } Vector& operator = (const Vector& v) { if (this != &v) { destroy(); copy(v); } return *this; } void resize(int capacity) { if (capacity <= 0) return; T* vs = new T[capacity]; if (capacity <= this->capacity) { memcpy(vs, this->vals, capacity*sizeof(T)); } else { memset(vs, 0, capacity*sizeof(T)); memcpy(vs, this->vals, this->capacity*sizeof(T)); } this->capacity = capacity; this->vals = vs; if (capacity < this->size_) size_ = capacity; } void push_back(T val) { if (capacity == 0) resize(2); else if (size_ >= capacity) resize(capacity * 2); vals[size_++] = val; } const T& operator [](int index) const { return vals[index]; } T& operator[](int index) { return vals[index]; } void clear() { size_ = 0; } bool empty() { return size_ == 0; } bool null() { return capacity == 0; } int size() { return size_; } void erase(int index) { if (index < 0 || index >= size_) return; forward_move(index + 1, --size_, index); } void erase(int start, int end) { if (start < 0 || start >= size_) return; if (end < 0 || end >= size_) return; if (start > end) return; forward_move(end + 1, size_ - 1, start); size_ -= (end - start + 1); } int find_first(T val) { for (int i = 0; i < size_; ++i) { if (vals[i] == val) return i; } return -1; } int find_last(T val) { for (int i = size_ - 1; i >= 0; --i) { if (vals[i] == val) return i; } return -1; } Vector<int> find_all(T val) { Vector<int> v; for (int i = 0; i < size_; ++i) { if (vals[i] == val) v.push_back(i); } return v; } };}#endif
先來類比不含傳回值的委託Action:
#ifndef _ACTION_H_#define _ACTION_H_#include "Vector.h"using namespace MyDataStructure;//聲明中和變參函數差不多,加上...,像下面這樣,這時可以把Types稱為類型包,//編譯器會根據執行個體化模板時自動解析Types到底包含了幾個類型,分別是哪些類//型。template <typename ... Types>class Action{private: //用類型包聲明一個指示變參函數的函數指標,操作符的支援的關鍵就是它了 //typedef 關鍵字不能省,否則下一句typedef語句非法,因為編譯器不能 //識別function為一個類型,用Types作為函數參數的類型,把paras稱為參 //數包吧 typedef void(*function)(Types...paras); typedef function func; Vector<func> funcs;public: Action& operator = (const func f) { funcs.clear(); return (*this += f); } Action& operator += (const func f) { funcs.push_back(f); return *this; } Action& operator -= (const func f) { funcs.erase(funcs.find_last(f)); return *this; } //實現委託嘛,當然用C++裡的仿函數了,那麼實現()操作符, //注意參數聲明形式 void operator()(Types ...paras) { int count = funcs.size(); for (int i = 0; i < count; i++) { //調用時,講參數包後的...加上,否則編譯器也不能識別, //正式以這種形式使得編譯器能夠對參數包進行解包的 funcs[i](paras...); } }};#endif
接下來,類比具有傳回值的委託Func:
#ifndef _FUNC_H_#define _FUNC_H_#include "Vector.h"using namespace MyDataStructure;//和可變參函數一樣,變參模板允許有幾個確定的參數聲明,但是這些//確定的必須放在可變的參數之前template<typename T1,typename ... Types>class Func{private: typedef T1 (*function)(Types ...paras); typedef function func; Vector<func> funcs;public: Func& operator = (const func f) { funcs.clear(); return (*this += f); } Func& operator += (const func f) { funcs.push_back(f); return *this; } Func& operator -= (const func f) { funcs.erase(funcs.find_last(f)); return *this; } //不用太擔心返回Vector拷貝建構函式帶來的開銷,編譯器的NRV最佳化 //會協助我們,當然以這樣的方式作為委託的傳回值未必合理 Vector<T1> operator()(Types ...paras) { Vector<T1> v; int count = funcs.size(); for (int i = 0; i < count; i++) { v.push_back(funcs[i](paras...)); } return v; }};#endif
類比完畢,看看測試代碼:
// Vector.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include "Vector.h"#include "Action.h"#include "Func.h"#include <iostream>using namespace MyDataStructure;using namespace std;void g1(int a, int b){ cout << a + b << endl;}void g2(int a, int b){ cout << a - b << endl;}void g3(int a, int b){ cout << a * b << endl;}void f1(int p){ cout << p << endl;}void f2(int p){ cout << p*2 << endl;}void f3(int p){ cout << p * 3 << endl;}int F1(){ return 5;}int F2(){ return 10;}int F3(){ return 15;}void Fv1(){ cout << 100 << endl;}void Fv2(){ cout << 200 << endl;}void Fv3(){ cout << 300 << endl;}int _tmain(int argc, _TCHAR* argv[]){ Action<int> ac; ac += f1; ac += f2; ac += f3; ac(4); ac -= f1; ac(5); Action<> acv; acv += Fv3; acv += Fv2; acv += Fv1; acv(); Action<int, int> cal; cal += g1; cal += g2; cal += g3; cal(50, 30); cal -= g3; cal(50, 30); Func<int> fu; fu += F1; fu += F2; fu += F3; Vector<int> v = fu(); for (int i = 0; i < v.size(); ++i) { cout << v[i] << ‘ ‘; } cout << endl; fu -= F1; v = fu(); for (int i = 0; i < v.size(); ++i) { cout << v[i] << ‘ ‘; } cout << endl; return 0;}
運行結果與預期一致,類比成功,在使用方式上,除了對於無傳回值無參數的委託有點不一樣,其他都是一樣的,並且C++類比的委託可以支援的類型個數絕對不止16個,這的看編譯器支援的是多少!這種方式類比委託還是多虧了C++11增加的新特性,否則必定還得話費很多時間才能完成。是時候多瞭解一下C++11了,這一次讓我更喜歡C++這門語言了。
每日一題20:與C++11的第一次邂逅——可變參模板與C#委託類比