这篇文章本来是前天发的,但是不知道为什么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#委托模拟
原文地址:http://blog.csdn.net/liao_jian/article/details/44993497