7.1类模板

类模板和虚函数,都是实现多态的重要方法。

类模板,锁定算法、步骤,偏重类型相同。

虚函数,偏重步骤不相同。

9.3类模板

类模板用于实现类所需数据的类型参数化。

类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响。

类模板声明的一般方法如下:

template<类模板参数>class 类名{//类体};

用类模板定义对象的一般格式如下:

类名<模板实例化参数类别>对象名(构造函数实参列表);

//类模板实际上对于使用不同的数据类型,但是操作方法一样的类的一种抽象

//类模板实现通用

 1 #include <iostream>
 2 
 3 //类模板实际上对于使用不同的数据类型,但是操作方法一样的类的一种抽象
 4 //类模板实现通用
 5 template <class T>
 6 class com
 7 {
 8 public:
 9     T a;
10     T b;
11     T add()
12     {
13         std::cout << typeid(T).name() << std::endl;
14         return a + b;
15     }
16 };
17 
18 void main()
19 {
20     com<int>comx;
21 
22     comx.a = 19;
23     comx.b = 29;
24 
25     std::cout << comx.add() << std::endl;
26 
27     system("pause");
28 }

//定义两种数据类型的类模板

//STL数据结构,算法,适用任何类型

 1 #include <iostream>
 2 #include <string>
 3 
 4 //定义两种数据类型的类模板
 5 //STL数据结构,算法,适用任何类型
 6 template<class T1, class T2>
 7 class myclass
 8 {
 9 public:
10     T1 t11;
11     T2 t22;
12     myclass(T1 t111, T2 t222) :t11(t111), t22(t222)
13     {
14 
15     }
16     void print()
17     {
18         std::cout << t11 << " " << t22 << std::endl;
19     }
20 };
21 
22 void main()
23 {
24     myclass<int, double>my1(10, 20.8);
25     my1.print();
26 
27     myclass<double, std::string>my2(20.8, "123456abc");
28     my2.print();
29 
30     system("pause");
31 }

//类模板可以有一个默认的值,C++11

 1 #include "myArray.h"
 2 
 3 template<class T = int>//类模板可以有一个默认的值,C++11
 4 myArray<T>::myArray()
 5 {
 6     std::cout << "构造" << typeid(T).name() << std::endl;
 7 }
 8 
 9 template<class T = int>//类模板可以有一个默认的值,C++11
10 myArray<T>::~myArray()
11 {
12     std::cout << "销毁" << typeid(T).name() << std::endl;
13 }

9.3.2类模板作函数参数

函数的形式参数类型可以是类模板或类模板的引用。对应的实际参数是该类模板实例化的模板类对象。

当一个函数拥有类模板参数时,这个函数必定是函数模板。

9.3.3在类层次中的类模板

类模板派生普通类,在定义派生类时要对基类的抽象类参数实例化。

从普通类派生模板类,意味着派生类添加了抽象类数据成员。

一个类模板在类层次结构中既可以是基类也可以是派生类:

1类模板可以从模板类派生。

2类模板可以从非模板类派生。

3模板类可以从类模板派生。

4非模板类可以从类模板派生。

//类模板可以直接继承类模板,但是类型必须传递

//普通类继承类模板,需要明确类型实例化类模板

//类模板继承普通类,常规操作方式

//类模板当作普通类使用,需要模板参数实例化

类模板->类模板

 1 #include <iostream>
 2 #include <string>
 3 
 4 //模板类的继承
 5 
 6 template <class T>
 7 class myclass//基类
 8 {
 9 public:
10     T x;
11     myclass(T t) :x(t)//构造函数
12     {
13 
14     }
15     void print()
16     {
17         std::cout << x << std::endl;
18     }
19 };
20 
21 template <class T>
22 class newclass :public myclass<T>//派生类
23 {
24 public:
25     T y;
26     newclass(T t1, T t2) :myclass(t1), y(t2)//构造函数
27     {
28 
29     }
30     void print()
31     {
32         std::cout << x << " " << y << std::endl;
33     }
34 };
35 
36 void main()
37 {
38     newclass<double>my1(10.9, 2.3);
39     my1.print();
40 
41     newclass<std::string>my2("abc", "xyz");
42     my2.print();
43     
44     system("pause");
45 }

普通类->类模板

 1 #include <iostream>
 2 #include <string>
 3 
 4 class xyz//基类
 5 {
 6 public:
 7     int x;
 8     int y;
 9     int z;
10     xyz(int a, int b, int c) :x(a), y(b), z(c)//构造函数
11     {
12 
13     }
14     void print()
15     {
16         std::cout << x << " " << y << " " << z << std::endl;
17     }
18 };
19 
20 template<class T>
21 class newxyz :public xyz//派生类
22 {
23 public:
24     T a;
25     newxyz(T t1, int a1, int b1, int c1) :xyz(a1, b1, c1), a(t1)//构造函数
26     {
27 
28     }
29     void print()
30     {
31         std::cout << "Ta=" << a << std::endl;
32         std::cout << x << " " << y << " " << z << std::endl;
33     }
34 };
35 
36 void main()
37 {
38     std::string str1 = "china";
39     newxyz<std::string> new1(str1, 10, 90, 89);
40     new1.print();
41     
42     system("pause");
43 }

普通类->类模板->普通类

 1 #include <iostream>
 2 #include <string>
 3 
 4 //类模板可以直接继承类模板,但是类型必须传递
 5 //普通类继承类模板,需要明确类型实例化类模板
 6 //类模板继承普通类,常规操作方式
 7 //类模板当作普通类使用,需要模板参数实例化
 8 
 9 class xyz//父类
10 {
11 public:
12     int x;
13     int y;
14     int z;
15     xyz(int a, int b, int c) :x(a), y(b), z(c)//构造函数
16     {
17 
18     }
19     void print()
20     {
21         std::cout << x << " " << y << " " << z << std::endl;
22     }
23 };
24 
25 template<class T>
26 class newxyz :public xyz//子类
27 {
28 public:
29     T a;
30     newxyz(T t1, int a1, int b1, int c1) :xyz(a1, b1, c1), a(t1)//构造函数
31     {
32 
33     }
34     void print()
35     {
36         std::cout << "Ta=" << a << std::endl;
37         std::cout << x << " " << y << " " << z << std::endl;
38     }
39 };
40 
41 class classrun :public newxyz<int>//孙类
42 {
43 public:
44     int d = 1000;
45     classrun(int a2, int b2, int c2, int d2) :newxyz<int>(a2, b2, c2, d2)//构造函数
46     {
47 
48     }
49     void print()
50     {
51         std::cout << d << " " << x << " " << y << " " << z << " " << a << std::endl;
52     }
53 };
54 
55 void main()
56 {
57     classrun run1(1, 2, 3, 4);
58     run1.print();
59 
60     system("pause");
61 }
62 
63 void main3()
64 {
65     std::string str1 = "china";
66     newxyz<std::string> new1(str1, 10, 90, 89);
67     new1.print();
68     
69     system("pause");
70 }

面试:什么是模板抽象类?

模板抽象类,不能说明抽象类的对象,但可以说明指向抽象类对象的指针(或引用)

 1 #include <iostream>
 2 
 3 template <class T>//模板抽象类,不能说明抽象类的对象,但可以说明指向抽象类对象的指针(或引用)
 4 class myclass
 5 {
 6 public:
 7     T x;
 8     myclass(T t) :x(t)//构造函数
 9     {
10 
11     }
12     virtual void print() = 0;//纯虚函数
13 };
14 
15 template <class T>
16 class newclass :public myclass<T>
17 {
18 public:
19     T y;
20     newclass(T t1, T t2) :myclass(t1), y(t2)//构造函数
21     {
22 
23     }
24     virtual void print()//虚函数
25     {
26         std::cout << x << " " << y << std::endl;
27     }
28 };
29 
30 void main()
31 {
32     myclass<int> *p = new newclass<int>(10, 9);//模板抽象类,不能说明抽象类的对象,但可以说明指向抽象类对象的指针(或引用)
33     p->print();//10 9
34     
35     system("pause");
36 }

9.3.4类模板与友元

在类模板中可以声明各种友元关系

一个函数或函数模板可以是类或类模板的友元。

一个类或类模板可以是类或类模板的友元类。

声明这种模板之间的友元关系符号比较烦琐。

1友元函数在类模板的内部定义

2友元函数在类模板的外部定义

1友元函数在类模板的内部定义

operator友元运算符重载+,实现两个类的对象的x和y分别相加

 1 #include <iostream>
 2 
 3 template <class T>
 4 class myclass
 5 {
 6 private:
 7     T x;
 8     T y;
 9 public:
10     myclass(T t1, T t2) :x(t1), y(t2)//构造函数
11     {
12 
13     }
14     friend void print(myclass<T> &my)//友元函数在类模板的内部定义
15     {
16         std::cout << my.x << " " << my.y << std::endl;
17     }
18     friend myclass * operator+(const myclass<T> &my1, const myclass<T> &my2)//重载+
19     {
20         myclass *p = new myclass(my1.x + my2.x, my1.y + my2.y);//创建在堆上,为了返回
21         return p;        
22     }
23 };
24 
25 void main()
26 {
27     myclass<int>my1(19, 29);
28     myclass<int>my2(11, 1);
29 
30     myclass<int> *pclass = my1 + my2;//重载+
31 
32     print(*pclass);//30 30
33         
34     system("pause");
35 }

2友元函数在类模板的外部定义

//如果友元函数在类模板的外部定义,第1声明要加类型T,第2必须上方声明类和函数

 1 #include <iostream>
 2 template<class T> class myclass;//声明类
 3 template<class T> void print(myclass<T> & my);//声明函数
 4 
 5 //如果友元函数在类模板的外部定义,第1声明要加类型T,第2必须上方声明类和函数
 6 
 7 template<class T>
 8 class myclass
 9 {
10 private:
11     T x;
12 public:
13     myclass(T t) :x(t)
14     {
15 
16     }
17     friend void print<T>(myclass<T> & my);//声明友元函数
18 };
19 
20 template<class T>
21 void print(myclass<T> & my)//定义友元函数
22 {
23     std::cout << my.x << std::endl;
24 }
25 
26 void main()
27 {
28     myclass<int>my1(10);
29     myclass<double>my2(10.9);
30 
31     print(my1);//10
32     print(my2);//10.9
33 
34     system("pause");
35 }

类模板和友元类

 1 #include <iostream>
 2 template<class T> class runclass;//声明友元类
 3 
 4 //友元类必须声明类的存在
 5 //需要声明友元类,必须要与类型相关
 6 
 7 template<class T>
 8 class myclass
 9 {
10 private:
11     T x;
12 public:
13     myclass(T t) :x(t)
14     {
15 
16     }
17     friend class runclass<T>;//声明友元类
18 };
19 
20 template<class T>
21 class runclass//定义友元类
22 {
23 public:
24     void print(const myclass<T> & my)//打印不修改,const引用
25     {
26         std::cout << my.x << std::endl;
27     }
28 };
29 
30 void main()
31 {
32     myclass<double>my1(10.9);
33     runclass<double>run1;
34 
35     run1.print(my1);
36     
37     system("pause");
38 }

面试,类模板当作类模板的参数

 1 #include <iostream>
 2 #include <string>
 3 
 4 //面试,类模板当作类模板的参数
 5 
 6 template<class T>
 7 class ren//一个通用类型的类模板
 8 {
 9 public:
10     T name;
11     ren(T t) :name(t)//构造函数
12     {
13 
14     }
15 };
16 
17 template<template<class T>class T1>
18 class people//类模板当作类模板的参数
19 {
20 public:
21     T1<std::string>t1x = "123";//T1必须实例化,必须结合
22     T1<std::string>num = "ABC";//等价于ren类型
23     people(T1<std::string>t1)//构造函数
24     {
25         std::cout << typeid(t1).name() << std::endl;//打印类型
26         std::cout << typeid(T1).name() << std::endl;//打印类型
27     }
28 };
29 
30 void main()
31 {
32     ren<std::string>ren1("hello");//基本数据类型
33     people<ren>people1(ren1);
34 
35     std::cout << people1.t1x.name << std::endl;
36     std::cout << people1.num.name << std::endl;
37     
38     system("pause");
39 }

9.3.5类模板与static成员

从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员。

和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化。

每个模板类有自己的类模板的static数据成员副本。

//类模板的static静态数据成员,访问方式:1对象,2类名<数据类型>

//不同数据类型的静态数据成员,地址不一样

//相同数据类型的静态数据成员,地址一样

 1 #include <iostream>
 2 #include <string>
 3 
 4 //类模板的static静态数据成员,访问方式:1对象,2类名<数据类型>
 5 //不同数据类型的静态数据成员,地址不一样
 6 //相同数据类型的静态数据成员,地址一样
 7 
 8 template<class T>
 9 class myclass
10 {
11 public:
12     static int num;//声明静态数据成员
13     T a;
14     myclass(T t) :a(t)//构造函数
15     {
16         num++;//计数器
17     }
18 };
19 
20 template<class T>
21 int myclass<T>::num = 0;//初始化静态数据成员
22 
23 void main()
24 {
25     myclass<int>my1(10);
26     myclass<double>my2(10.9);
27     myclass<std::string>my3("1234");
28     myclass<int>my4(10);
29 
30     //访问方式:1对象
31     std::cout << my1.num << std::endl;//2
32     std::cout << my2.num << std::endl;//1
33     std::cout << my3.num << std::endl;//1
34     std::cout << my4.num << std::endl;//2
35 
36     //发现<int>的地址一样
37     std::cout << &my1.num << std::endl;//一样
38     std::cout << &my2.num << std::endl;
39     std::cout << &my3.num << std::endl;
40     std::cout << &my4.num << std::endl;//一样
41     
42     //访问方式:2类名<数据类型>
43     std::cout << myclass<int>::num << std::endl;
44     
45     system("pause");
46 }

类模板以及类模板嵌套

一个普通类的对象作为一个类模板的成员,可以紧跟在类说明之后进行定义

一个类模板的对象作为一个类模板的成员,不可以紧跟在类说明之后进行定义

 1 #include <iostream>
 2 
 3 template<class T>
 4 class myclass
 5 {
 6 public:
 7     class newclass
 8     {
 9     public:
10         int num;
11     }new1;//一个普通类的对象作为一个类模板的成员,可以紧跟在类说明之后进行定义
12 
13     template<class V>
14     class runclass
15     {
16     public:
17         V v1;
18     };//一个类模板的对象作为一个类模板的成员,不可以紧跟在类说明之后进行定义
19     runclass<T>t1;//先说明类,再单独进行变量定义
20     runclass<double>t2;//先说明类,再单独进行变量定义
21 };
22 
23 void main()
24 {
25     myclass<int> my1;
26     my1.new1.num = 10;
27 
28     my1.t1.v1 = 12;
29     my1.t2.v1 = 12.9;
30 
31     std::cout << my1.t1.v1 << std::endl;//12
32     std::cout << my1.t2.v1 << std::endl;//12.9
33 
34     system("pause");
35 }

小结

模板是C++类型参数化的多态工具。C++提供函数模板和类模板。

模板定义以模板说明开始。类属参数必须在模板定义中至少出现一次。

同一个类属参数可以用于多个模板。

类属参数可用于函数的参数类型、返回类型和声明函数中的变量。

模板由编译器根据实际数据类型实例化,生成可执行代码。实例化的函数模板称为模板函数;实例化的类模板称为模板类。

函数模板可以用多种方式重载。

类模板可以在类层次中使用。

原文地址:https://www.cnblogs.com/denggelin/p/5671693.html