代码验证:
#include<iostream> using namespace std; class A1 { public: void print1() { cout << "A1在被调用" << endl; } }; class A2 { public: void print2() { cout << "A2在被调用" << endl; } }; template<class T> class A { public: void print1() { T a; a.print1(); } void print2() { T a.print2(); } }; void test1() { A<A1> a; a.print1(); a.print2(); } int main() { test1(); return 0; }
代码在没有运行的时候,没有报错,在运行之后,会发现内部编译错误,是因为通用类型被定义为A1,A1中没有print2函数,现在注释掉,在来看看:
#include<iostream> using namespace std; class A1 { public: void print1() { cout << "A1在被调用" << endl; } }; class A2 { public: void print2() { cout << "A2在被调用" << endl; } }; template<class T> class A { public: void print1() { T a; a.print1(); } //void print2() //{ // T a.print2(); //} }; void test1() { A<A1> a; a.print1(); //a.print2(); } int main() { test1(); return 0; }
可以调用,这就是因为在确定通用类型之前没有生成内部函数,在确定好之后,会创建成员函数。
类模板对象做函数参数
类模板实例化出来的对象,可以做函数的参数吗?可以而且方法还不少:
1.指定传入类型:直接显示对象的数据类型
2.参数模板化:将对象中的参数变成模板进行传递
3.整个类模板化:将整个对象模型模板化进行传递
指定传入类型
指定传入类型怎么去做?下面用代码来演示:
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; void print(per<string, int> p) { cout << p._name << "的年龄" << p._age << "岁" << endl; } void test1() { per<string, int> p("张三",18); print(p); } int main() { test1(); return 0; }
直接将对象名前面的类名和模板的数据类型传过去就可以。
参数模板化
参数模板化,顾名思义,就是将参数变成和模板一样,模板有什么特点?通用的数据类型,就其实就是将模板的参数列表变成通用的数据类型即可:
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; template<class T1,class T2> void print(per<T1,T2> p) { cout << p._name << "的年龄" << p._age << "岁" << endl; } void test1() { per<string, int> p("张三",18); print(p); } int main() { test1(); return 0; }
将对象模型进行模板化
将对象模型进行模板化,就是将对象变成一个通用类型:
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; template<class T> void print(T p) { cout << p._name << "的年龄" << p._age << "岁" << endl; } void test1() { per<string, int> p("张三",18); print(p); } int main() { test1(); return 0; }
类模板和继承
既然都是类,那类模板可以作为父类吗?被继承吗?是可以的,但是有需要注意的地方:
1.当子类继承的父类是一个类模板的时候,子类在声明的时候,要指出父类中通用类型的类型
2.如果不指定,编译器无法给子类分配内存
3.如果想灵活的指出父类中通用类型的类型,子类也需要变成类模板
第一点和第二点
因为在子类中会继承父类中的所有非静态成员变量,这个时候如果不知道父类中成员是什么类型,则编译器不知道应该分配多少内存给子类当中,所以要指定出来,那怎么指定呢?写法:
class 子类 : 继承方式 父类< 指定出类型 >
代码验证:
#include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; class per1 :public per { public: }; int main() { return 0; }
这个时候没有写,报错。
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; class per1 :public per<string ,int> { public: }; int main() { return 0; }
写上之后不报错。
灵活指出父类中的类型
那怎么样才能灵活的指出呢?这个时候将子类变成模板即可:
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; } T1 _name; T2 _age; }; template<class T,class T1> class per1 :public per<T,T1> { public: }; int main() { return 0; }
这个时候,这个T和T1就指向的是父类中的两个通用模板:
类模板成员函数类外实现
普通类的成员函数可以在类外实现,那类模板的是否可以?是可以的,有一定的语法格式,不仅仅要加作用域:
template < class T >
返回类型 类名< 参数 >:: 函数名 (T ____)
构造函数也可以进行类外实现,用代码验证一下:
#include<string> #include<iostream> using namespace std; template<class T1,class T2> class per { public: per(T1 a, T2 b); T1 _name; T2 _age; }; template<class T1,class T2> per<T1, T2>::per(T1 a, T2 b) { _name = a; _age = b; cout << _name << "的年龄为" << _age << endl; } void test1() { per<string, int>p("张三", 18); } int main() { test1(); return 0; }
类模板的分文件编写
当代码量过大时,程序员通常会选择分文件编写,将声明实现和调用分开,那模板分开可以正常使用吗?
验证:
test.cpp
#include"per.h" void test1() { per<string, int>p("张三", 18); } int main() { test1(); return 0; }
per.cpp
#include"per.h" template<class T1, class T2> class per { public: per(T1 a, T2 b) { _name = a; _age = b; cout << _name << "的年龄为" << _age << endl; } T1 _name; T2 _age; };
per.h
#pragma once #include<string> #include<iostream> using namespace std; template<class T1, class T2> class per;
是报错的,那这是为什么?上面说到,对于类模板中的成员函数在编译器阶段才去创建的,我们包含的头文件,那这个时候编译器就不会看到实现部分,只看到了声明,那这个时候可以将引的头文件该成实现的模块:
#include"per.cpp" void test1() { per<string, int>p("张三", 18); } int main() { test1(); return 0; }
是可以正常使用的,那这个时候还可以将声明和实现放在一起,任何将后缀改为hpp,其他也可以,只是hpp是约定俗成的,一看上去就知道是模板的实现和定义。
类模板和友元
那类模板可以有友元函数吗?是可以拥有的,分为类内实现和类外实现
类内实现
类内实现直接在类捏将函数功能实现出来即可:
template<class T1,class T2> class per { friend void print(per<T1, T2> &p) { cout << _name << "的年龄为" << _age << endl; } public: per(T1 a, T2 b) { _name = a; _age = b; } private: string _name; int _age; };
类外实现
template<class T1,class T2> class per { friend void print< >(per<T1, T2>& p); public: per(T1 a, T2 b) { _name = a; _age = b; } private: string _name; int _age; };
类外实现时要注意,需要提前让编译器知道它存在,所以声明在类模板中。
注意
掌握模板不是为了写模板,而是为了使用第九层中的stl。
第九层…顶层?
看完上面的内容,我便踏入了前往第九层的楼梯,上来之后,抬头便是许久未见的天空…