這篇文章本來是前天發的,但是不知道爲什麼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++這門語言了。