PoEdu培训 STL班 第三课 模板编程(三) 特化
文章类别: 培训笔记 0 评论

PoEdu培训 STL班 第三课 模板编程(三) 特化

文章类别: 培训笔记 0 评论

STL(3)

泛型和多态

泛型其实也是一种多态.

多态的好处:可以有一个统一的接口

多态分为两种:

泛型编程小技巧

在使用VS进行泛型编程的时候, 比如如下代码:

#include <vector>

namespace Hades
{
    template <typename T, typename CON = std::vector<T> >
    class HadesStack
    {
    public:
        void PushBack(const T& ele)
        {
            c_.push_back(ele);
        }

    private:
        CON c_; // 使用的容器
    }
}

在我们进行编程的时候, 由于VS不知道我们使用的是什么类型, 所以无法进行自动提示.
这时, 我们将 CON c_ 先注释掉, 改成 std:vector\<T\> c_ 就可以自动提示代码了.
等我们编写完成后, 一定要记得将我们注释掉的 CON c_ 改回来.

成员模板

template<typename T>
class HadesType
{
private:
    
public:
    T data_;

    HadesType(const T data) : data_(data) {}

    void Assign(HadesType other)
    {
        // 不考虑浅拷贝
        data_ = other.data_;
    }
}

int main()
{
    HadesType<int> intType(1);
    HadesType<double> doubleType(2.01);

    intType.Assign(doubleType); // 报错, 类型不匹配

    return 0;
}

解析, 为什么会不行呢?

因为我们的模板, 会经过2次编译.
首先, 我们的HadesType完全没有语法错误. 
然后, 在编译期间, 会生成两个完全不同的类型.   

当我们在使用Assign的时候, 我们的类型发生了变化.
相当于, HadesTypeInt.Assign(HadesTypeDouble); 
这样当然是错误的, 因为我们要 HadesTypeInt.Assign(HadesTypeInt);

解决办法:
我们这种操作是很多的, 所以我们需要想办法进行实现.

// 我们将Assign改造一下
template<typename X>
void Assign(X other)
{
    // 不考虑浅拷贝
    data_ = other.data_;
}

现在, 我们就可以进行赋值操作了.

如果我们要将方法和声明分离, 则写成如下模式:

void Assign(HadesType other);   // 类中的声明

// 分离实现的写法
template <typename T>   // 类模板
template <typename X>   // 成员模板
     // 类名            // X: 参数类型
void HadesType<T>::Assign(X other)
{
    // ....
}

注意, 我们的两个 template 缺一不可, 并且不可写到一起

但是, 当我们的 T data_private 的时候, 我们就不能通过这种方法来进行了.
因为我们的 data_ 在类的外部依然无法访问.

一定要记住, 我们的模板编程, 所看到的类不是我们原来认识的那个类

那么, 我们在 T data_ 私有的时候, 应该怎么解决呢?

// 在我们已经修改的基础上, 我们再新增一个函数
const T& GetData() const
{
    return data_;
}

这样, 我们就完美解决了我们 Assign 的问题了.

成员模板可以在任何一个类中出现

class Demo
{
public:
    template <typename T>   // 成员模板
    const T& GetVal(const T& val)
    {
        return val;
    }
}

此时, 如果我们需要声明和实现分离的话, 应该这么写

class Demo
{
public:
    template <typename T>   // 成员模板
    const T& GetVal(const T& val);
};

template <typename T>   // 成员模板
const T& Demo::GetVal(const T& val)
{
    return val;
}

注意, 我们的 Demo 是没有泛型的.

类的全特化

全特化的例子:

#include <iostream>

template <typename T>
class Array
{
private:
    T data_;

public:
    Array()
    {
        std::cout << "Array<T>...." << std::endl;
    }
    ~Array()
    {
        std::cout << "~Array<T>...." << std::endl;
    }
    const T& GetValue() const
    {
        return data_;
    }
};

// 类的全特化
template <>
class Array<int*>
{
private:
    int* data_;

public:
    Array()
    {
        data_ = new int;
        std::cout << "Array<int*>...." << std::endl;
    }
    ~Array()
    {
        delete data_;
        std::cout << "~Array<int*>...." << std::endl;
    }
};

int main()
{
    Array<int> intArr;
    Array<int*> intpArr;

    return 0;
}

执行结果:
Alt 执行结果

接下来, 我们调用一下 GetValue 函数:


int main()
{
    Array<int> intArr;
    Array<int*> intpArr;

    // 编译错误, Array<int*>中没有这个函数.
    std::cout << intpArr.GetValue() << std::endl;

    return 0;
}

全特化的类与我们的模板类没有任何关系, 实现的方法 _可以不相同_.
类的全特化并 不是继承

如果我们想调用 intpArr.GetValue(), 我们需要在 Array<int*> 中进行函数的实现.

我们对模板类进行了全特化后, 模板类就不会在编译时进行生成相应的类
特化的类是一个全新的类, 需要的任何功能都需要自己来实现

类的偏特化

偏特化的例子:

#include <iostream>

template <typename T>
class Array
{
private:
    T data_;

public:
    Array()
    {
        std::cout << "Array<T>...." << std::endl;
    }
    ~Array()
    {
        std::cout << "~Array<T>...." << std::endl;
    }
    const T& GetValue() const
    {
        return data_;
    }
};

// 类的偏特化
template <>
Array<int*>::Array()
{
    data_ = new int;
    std::cout << "Array<int*>...." << std::endl;
}
// 类的偏特化
template <>
Array<int*>::~Array()
{
    delete data_;
    std::cout << "~Array<int*>...." << std::endl;
}


int main()
{
    Array<int> intArr;
    Array<int*> intpArr;

    return 0;
}

执行结果:
Alt 执行结果

接下来, 我们调用一下 GetValue 函数:


int main()
{
    Array<int> intArr;
    Array<int*> intpArr;

    // OK, 成功打印
    std::cout << intpArr.GetValue() << std::endl;

    return 0;
}

执行结果:
Alt 执行结果

类的偏特化是 _只对当前模板类中的某些特定的类中的某些方法的特化_, 进行有意识的特化
偏特化 特化的是一个方法_, _其他的代码还是由编译器来生成
偏特化的方法, 编译器 不会 进行生成

多模板参数的偏特化

暂略....

模板类的继承

template<typename T>
class Base
{

};

class Demo : public Base<int>
{

};

template<typename T>
class Child : public Base<T>
{

};

单例模式

通过包装器的方式, 使用模板类编写一个单例模式.

#include <iostream>
using namespace std;

template<typename T>
class Singleton     // 单例模式的模板类
{
public:
    static T& GetInstance()
    {
        if(!pInstance)
        {
            pInstance = new T;
        }
        return *pInstance;
    }
private:
    Singleton();
    static T* pInstance;
};

template<typename T>
T* Singleton<T>::pInstance = nullptr;

class Demo
{
public:
    void Dump()
    {
        cout << "Dump!!!!" << endl;
    }
}

int main()
{
    Singleton<Demo>::GetInstance().Dump();
    return 0;
}

未完待续...

如有错误,请提出指正!谢谢.

回复