每日一題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++這門語言了。

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