c++:模板的类型萃取

首先,需要举一个例子来说明c++在什么情况下需要对不同的类型不同对待。

template <class T>
class SeqList
{
public:
    SeqList()
        :_a(NULL)
        , _size(0)
        , _capacity(0)
    {}

    void CheckCapacity()
    {
        if (_size >= _capacity)
        {
            _capacity = _capacity > 0 ? _capacity * 2 : 3;//当容量为0时增容为3,之后每次增2倍
            T* newA = new T[_capacity];
            if (_a != NULL)
            {
                memcpy(newA, _a, _size*sizeof(T));//使用memcpy
            }
            delete[] _a;
            _a = newA;

        }
    }

    void PushBack(const T& x)
    {
        CheckCapacity();
        _a[_size++] = x;
    }

    void Print()
    {
        for (size_t i = 0; i < _size; i++)
        {
            cout << _a[i] << " ";
        }
        cout << endl;
    }

private:
    T* _a;
    size_t _size;
    size_t _capacity;
};

这是一个模板类的顺序表,实现了尾插和增容两个接口。
接下来进行测试:

void TestSeqList()
{
    SeqList<int> s1;
    s1.PushBack(1);
    s1.PushBack(2);
    s1.PushBack(3);
    s1.PushBack(4);
    s1.Print();
}

它是没有问题的:
这里写图片描述

但是如果里面存string类型:
这里写图片描述
可以看到,它崩溃了,而且打印出来了数据,说明是在析构的时候崩溃,到底怎么回事?

这里写图片描述

如图,因为使用得是memcpy,所以新开辟的空间的前三个_str和_a指向的空间的每一个_str指向的一样。
这样就导致delete[] _a的时候,新开辟的空间的前三个_str为野指针,必然崩溃。
如果需要解决问题,只要这样做:

                for (size_t i = 0; i < _size; i++)
                {
                    newA[i] = _a[i];
                }

这里写图片描述
利用operator=,通过for循环依次赋值,这样新旧_str指向的空间不同,解决了问题。

现在就出现了需要对不同的类型不同对待的情况,因为memcpy的效率比for循环高得多,但是对特殊类型又不得不使用for循环。
使用全特化可以解决:

template <>
class SeqList<int>
{
public:
    SeqList()
        :_a(NULL)
        , _size(0)
        , _capacity(0)
    {}

    void CheckCapacity()
    {
        if (_size >= _capacity)
        {
            _capacity = _capacity > 0 ? _capacity * 2 : 3;
            int* newA = new int[_capacity];
            if (_a)
            {
                memcpy(newA, _a, _size*sizeof(int));
            }
            delete[] _a;
            _a = newA;
        }
    }

    void PushBack(const int& x)
    {
        CheckCapacity();
        _a[_size++] = x;
    }


~SeqList()
{
    delete[] _a;
    _size = _capacity = 0;
}

private:
    int* _a;
    size_t _size;
    size_t _capacity;
};
template <>
class SeqList<string>
{
public:
    SeqList()
        :_a(NULL)
        , _size(0)
        , _capacity(0)
    {}

    void CheckCapacity()
    {
        if (_size >= _capacity)
        {
            _capacity = _capacity > 0 ? _capacity * 2 : 3;
            string* newA = new string[_capacity];
            if (_a)
            {
                for (size_t i = 0; i < _size; i++)
                {
                    newA[i] = _a[i];
                }
                delete[] _a;
            }
            _a = newA;
        }
    }

    void PushBack(const string& x)
    {
        CheckCapacity();
        _a[_size++] = x;
    }

    ~SeqList()
    {
        delete[] _a;
        _size = _capacity = 0;
    }

private:
    string* _a;
    size_t _size;
    size_t _capacity;
};

这不是一个好办法,这样做会让代码的复用性很差,全是重复代码。
现在就该类型萃取出场了:

struct __TrueType//一个空类
{};

struct __FalseType//空类
{};

template <class T>
struct __TypeTraits
{
    typedef __FalseType ISPODType; //默认为不是基本类型 POD(基本类型)
};

template <>
struct __TypeTraits<int>
{
    typedef __TrueType ISPODType; //int是基本类型
};

template <class T>
T* TypeCopy(T* dst, const T* src, size_t n)
{
    return __TypeCopy(dst, src, n, __TypeTraits<T>::ISPODType());
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n,__TrueType)
{
    cout << "memcpy()" << endl;
    return (T*)memcpy(dst, src, n*sizeof(T));
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n, __FalseType)
{
    for (size_t i = 0; i < n; i++)
    {
        dst[i] = src[i];
    }
    cout << "operator=()" << endl;
    return dst;
}

想要读懂这段代码不容易,我总结一下:
这里写图片描述
这里写图片描述
通过TypeCopy,那么就可以根据类型来确定拷贝方案。

猜你喜欢

转载自blog.csdn.net/han8040laixin/article/details/78575736
今日推荐