每日一题20:与C++11的第一次邂逅——可变参模板与C#委托模拟

这篇文章本来是前天发的,但是不知道为什么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++这门语言了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章