- 概念:与其他类型一样,我们可以定义一个typedef来引用实例化的类
二、使用using为模板类型取别名提示:
由于模板不是一个类型,所以不能用typedef引用一个模板,在typedef时必须给出特定的类型
- typedef Blob<string> StrBlob; //正确
- typedef Blob<T> StrBlob; //错误
演示案例
template<typename T> class Blob { }; typedef Blob<string> StrBlob;//当为实例化为string类型的Blob类取别名 int main() { //下面两者等价 Blob<string> a; StrBlob b; return 0; }
- C++11标准允许使用using定义一个类型别名
演示案例
template<typename T> using twin = pair<T, T>; //取别名 twin<string> authors;//相当于pair<string,string> twin<int> authors;//相当于pair<int,int>
三、模板参数当我们定义一个模板类型别名时,可以固定一个或多个模板参数
- 使用时,只能指出pair的T类型,不能指定第二个默认固定的类型
template<typename T> using twin = pair<T, unsigned>; twin<string> authors;//相当于pair<string,unsigned> twin<int> authors;//相当于pair<int,unsigned>
模板参数与作用域的关系
- 模板参数的名称会覆盖其他名称
typedef double A; //为double数据类型取别名 template<typename A, typename B> void func(A a, B b) { A tmp = a; //tmp的类型为模板参数A的类型,而非double double B; //错误定义,重复声明模板参数B }
四、模板默认实参模板的声明与定义
- 与函数一样,模板也有声明和定义
//声明compare函数模板和Blob类模板 template<typename T> int compare(); template<typename T> class Blob; //定义compare函数模板 template<typename T> int compare() { } //定义Blob类模板 template<typename T> class Blob { };
- 概念:新标准规定,我们可以为函数和类模板提供默认实参。新标准以前只有类模板才可以提供默认实参
函数模板的默认实参
- 案例:下面有一个默认模板实参less<T>和一个默认参数实参F()
template <typename T,typename F=less<T>> int compare(const T &v1, const T &v2, F f = F()) { if (f(v1, v2)) return -1; if (f(v2, v1)) return -1; return 0; }
五、控制实例化(extern)类模板的默认实参
//类模板中默认为int类型 template <typename T = int>class Numbers { public: Numbers(T v=0):val(v){} private: T val; }; int main() { Numbers<> a; //使用默认的int类型 Numbers<double> b; //替换默认的int,使用double return 0; }
- 一个重要的概念:当模板被使用时才会进行实例化
- 一个非常低效的场景:当两个或多个独立编译的源文件使用了相同的模板,并提供了相同的模板参数时,则每个文件中都会有该末班的一个实例
显示实例化
- 在大多数系统中,在多个文件中实例化相同模板的额外开销可能非常严重。在新标准中,我们可以通过显式实例化来避免这种开销
- 显式实例化格式如下:
- declaration是一个类或函数声明,其中所有模板参数必须显式给出
- 例如:
- 这个还没看懂,以后补上,见C++ Primer P597
- 对于模板设计者所面对的设计选择,标准库只能指针类型给出了一个很好的展示
- shared_ptr参阅:javascript:void(0)
- unique_ptr参阅:javascript:void(0)
- shared_ptr与unique_ptr之间的明显不同在于它们所保管的指针的策略:
- shared_ptr:共享指针所有权
- unique_ptr:独占指针所有权
- shared_ptr与unique_ptr之间的另一个差异是它们允许用户重载默认删除器的方式:
- shared_ptr:为了重载一个shared_ptr的删除器,可以在创建时或者reset指针时传递给它一个可调用对象即可
- unique_ptr:与shared_ptr相反,unique_ptr的删除器的类型是unique_ptr类型的一部分,用户必须在定义unique_ptr时显式模板实参的形式提供删除器的类型
在运行时绑定删除器(shared_ptr)
- shared_ptr必须直接访问其删除器,即,删除器必须保存为一个指针或一个封装了指针的类(如function)
- 因此,shared_ptr不是将删除器直接保存为一个成员,因为删除器的类型直到运行时才知道。所以,在一起shared_ptr的生存期中,我们可以随时改变其删除器的类型(例如在定义时给shared_ptr一种删除器类型,随后使用reset赋予此shared_ptr另一种类型的删除器)
- 为了考察删除器是如何正确工作的,我们假设shared_ptr将它管理的指针保存在一个成员p中,且删除器是通过一个名为del的成员来访问的。则shared_ptr的析构函数必须包含类似下面的语句:
//伪代码 //del的值只有在运行时才知道:通过一个指针来调用它 del ? del(p) : delete p; //del(p)需要运行时跳转到del的地址
在编译时绑定删除器(unique_ptr)
- 在unique_ptr中,删除器的类型是类的一部分
- 因此,unique_ptr有两个模板参数:
- 一个用来表示它所管理的指针
- 另一个表示删除器的类型
- 由于删除器的类型是unique_ptr的一部分,因此删除器成员的类型在编译时就知道了,从而删除器可以直接保存在unique_ptr对象中
- unique_ptr的析构函数与shared_ptr类似,也是对其保存的指针调用用户提供的删除器或执行delete
//伪代码 //del在编译时绑定;直接调用实例化的删除器 del(p) //无运行时额外开销
- 我们在前一篇文章中就设计过一种unique_ptr的删除器DebugDelete(参阅:javascript:void(0))
-
总结:
- 通过在编译时绑定删除器,unique_ptr避免了间接调用删除器的运行时开销
- 通过在运行时绑定删除器,shared_ptr使用户重载删除器更为方便