文章内容转载自黑马程序员C++提高编程讲义,如有侵权,请联系作者删除
1.3 类模板
1.3.1 类模板语法
类模板作用:
建立一个通用类,类中的成员
数据类型可以不具体制定,用一个虚拟的类型 来代表。
语法:
解释:
template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
示例:
#include <string> template <class NameType , class AgeType > class Person { public : Person (NameType name, AgeType age) { this ->mName = name; this ->mAge = age; } void showPerson () { cout << "name: " << this ->mName << " age: " << this ->mAge << endl; } public : NameType mName; AgeType mAge; }; void test01 () { Person<string, int >P1 ("孙悟空" , 999 ); P1.showPerson (); } int main () { test01 (); system ("pause" ); return 0 ; }
总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
1.3.2 类模板与函数模板区别
类模板与函数模板区别主要有两点:
类模板没有自动类型推导的使用方式
类模板在模板参数列表中可以有默认参数
示例:
#include <string> template <class NameType , class AgeType = int > class Person{ public : Person (NameType name, AgeType age) { this ->mName = name; this ->mAge = age; } void showPerson () { cout << "name: " << this ->mName << " age: " << this ->mAge << endl; } public : NameType mName; AgeType mAge; }; void test01 () { Person <string ,int >p ("孙悟空" , 1000 ); p.showPerson (); } void test02 () { Person <string> p ("猪八戒" , 999 ); p.showPerson (); } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
总结:
类模板使用只能用显示指定类型方式
类模板中的模板参数列表可以有默认参数
1.3.3 类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
示例:
class Person1 { public : void showPerson1 () { cout << "Person1 show" << endl; } }; class Person2 { public : void showPerson2 () { cout << "Person2 show" << endl; } }; template <class T >class MyClass { public : T obj; void fun1 () { obj.showPerson1 (); } void fun2 () { obj.showPerson2 (); } }; void test01 () { MyClass<Person1> m; m.fun1 (); } int main () { test01 (); system ("pause" ); return 0 ; }
总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建
1.3.4 类模板对象做函数参数
学习目标:
一共有三种传入方式:
指定传入的类型 --- 直接显示对象的数据类型
参数模板化 --- 将对象中的参数变为模板进行传递
整个类模板化 --- 将这个对象类型 模板化进行传递
示例:
#include <string> template <class NameType , class AgeType = int > class Person{ public : Person (NameType name, AgeType age) { this ->mName = name; this ->mAge = age; } void showPerson () { cout << "name: " << this ->mName << " age: " << this ->mAge << endl; } public : NameType mName; AgeType mAge; }; void printPerson1 (Person<string, int > &p) { p.showPerson (); } void test01 () { Person <string, int >p ("孙悟空" , 100 ); printPerson1 (p); } template <class T1 , class T2 >void printPerson2 (Person<T1, T2>&p) { p.showPerson (); cout << "T1的类型为: " << typeid (T1).name () << endl; cout << "T2的类型为: " << typeid (T2).name () << endl; } void test02 () { Person <string, int >p ("猪八戒" , 90 ); printPerson2 (p); } template <class T>void printPerson3 (T & p) { cout << "T的类型为: " << typeid (T).name () << endl; p.showPerson (); } void test03 () { Person <string, int >p ("唐僧" , 30 ); printPerson3 (p); } int main () { test01 (); test02 (); test03 (); system ("pause" ); return 0 ; }
总结:
通过类模板创建的对象,可以有三种方式向函数中进行传参
使用比较广泛是第一种:指定传入的类型
1.3.5 类模板与继承
当类模板碰到继承时,需要注意一下几点:
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
如果不指定,编译器无法给子类分配内存
如果想灵活指定出父类中T的类型,子类也需变为类模板
示例:
template <class T >class Base { T m; }; class Son :public Base<int > { }; void test01 () { Son c; } template <class T1 , class T2 >class Son2 :public Base<T2>{ public : Son2 () { cout << typeid (T1).name () << endl; cout << typeid (T2).name () << endl; } }; void test02 () { Son2<int , char > child1; } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
总结:如果父类是类模板,子类需要指定出父类中T的数据类型
1.3.6 类模板成员函数类外实现
学习目标:能够掌握类模板中的成员函数类外实现
示例:
#include <string> template <class T1 , class T2 >class Person {public : Person (T1 name, T2 age); void showPerson () ; public : T1 m_Name; T2 m_Age; }; template <class T1 , class T2 >Person<T1, T2>::Person (T1 name, T2 age) { this ->m_Name = name; this ->m_Age = age; } template <class T1 , class T2 >void Person<T1, T2>::showPerson () { cout << "姓名: " << this ->m_Name << " 年龄:" << this ->m_Age << endl; } void test01 () { Person<string, int > p ("Tom" , 20 ) ; p.showPerson (); } int main () { test01 (); system ("pause" ); return 0 ; }
总结:类模板中成员函数类外实现时,需要加上模板参数列表
1.3.7 类模板分文件编写
学习目标:
掌握类模板成员函数分文件编写产生的问题以及解决方式
问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
示例:
person.hpp中代码:
#pragma once #include <iostream> using namespace std;#include <string> template <class T1 , class T2 >class Person {public : Person (T1 name, T2 age); void showPerson () ; public : T1 m_Name; T2 m_Age; }; template <class T1 , class T2 >Person<T1, T2>::Person (T1 name, T2 age) { this ->m_Name = name; this ->m_Age = age; } template <class T1 , class T2 >void Person<T1, T2>::showPerson () { cout << "姓名: " << this ->m_Name << " 年龄:" << this ->m_Age << endl; }
类模板分文件编写.cpp中代码
#include <iostream> using namespace std;#include "person.cpp" #include "person.hpp" void test01 () { Person<string, int > p ("Tom" , 10 ) ; p.showPerson (); } int main () { test01 (); system ("pause" ); return 0 ; }
总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp
1.3.8 类模板与友元
学习目标:
全局函数类内实现 - 直接在类内声明友元即可
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
示例:
#include <string> template <class T1 , class T2 > class Person ;template <class T1, class T2>void printPerson2 (Person<T1, T2> & p) { cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl; } template <class T1 , class T2 >class Person { friend void printPerson (Person<T1, T2> & p) { cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl; } friend void printPerson2<>(Person<T1, T2> & p); public : Person (T1 name, T2 age) { this ->m_Name = name; this ->m_Age = age; } private : T1 m_Name; T2 m_Age; }; void test01 () { Person <string, int >p ("Tom" , 20 ); printPerson (p); } void test02 () { Person <string, int >p ("Jerry" , 30 ); printPerson2 (p); } int main () { test02 (); system ("pause" ); return 0 ; }
总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别
1.3.9 类模板案例
案例描述: 实现一个通用的数组类,要求如下:
可以对内置数据类型以及自定义数据类型的数据进行存储
将数组中的数据存储到堆区
构造函数中可以传入数组的容量
提供对应的拷贝构造函数以及operator=防止浅拷贝问题
提供尾插法和尾删法对数组中的数据进行增加和删除
可以通过下标的方式访问数组中的元素
可以获取数组中当前元素个数和数组的容量
示例:
myArray.hpp中代码
#pragma once #include <iostream> using namespace std;template <class T >class MyArray { public : MyArray (int capacity) { this ->m_Capacity = capacity; this ->m_Size = 0 ; pAddress = new T[this ->m_Capacity]; } MyArray (const MyArray & arr) { this ->m_Capacity = arr.m_Capacity; this ->m_Size = arr.m_Size; this ->pAddress = new T[this ->m_Capacity]; for (int i = 0 ; i < this ->m_Size; i++) { this ->pAddress[i] = arr.pAddress[i]; } } MyArray& operator =(const MyArray& myarray) { if (this ->pAddress != NULL ) { delete [] this ->pAddress; this ->m_Capacity = 0 ; this ->m_Size = 0 ; } this ->m_Capacity = myarray.m_Capacity; this ->m_Size = myarray.m_Size; this ->pAddress = new T[this ->m_Capacity]; for (int i = 0 ; i < this ->m_Size; i++) { this ->pAddress[i] = myarray[i]; } return *this ; } T& operator [](int index) { return this ->pAddress[index]; } void Push_back (const T & val) { if (this ->m_Capacity == this ->m_Size) { return ; } this ->pAddress[this ->m_Size] = val; this ->m_Size++; } void Pop_back () { if (this ->m_Size == 0 ) { return ; } this ->m_Size--; } int getCapacity () { return this ->m_Capacity; } int getSize () { return this ->m_Size; } ~MyArray () { if (this ->pAddress != NULL ) { delete [] this ->pAddress; this ->pAddress = NULL ; this ->m_Capacity = 0 ; this ->m_Size = 0 ; } } private : T * pAddress; int m_Capacity; int m_Size; };
类模板案例—数组类封装.cpp中
#include "myArray.hpp" #include <string> void printIntArray (MyArray<int >& arr) { for (int i = 0 ; i < arr.getSize (); i++) { cout << arr[i] << " " ; } cout << endl; } void test01 () { MyArray<int > array1 (10 ) ; for (int i = 0 ; i < 10 ; i++) { array1.Push_back (i); } cout << "array1打印输出:" << endl; printIntArray (array1); cout << "array1的大小:" << array1.getSize () << endl; cout << "array1的容量:" << array1.getCapacity () << endl; cout << "--------------------------" << endl; MyArray<int > array2 (array1) ; array2.Pop_back (); cout << "array2打印输出:" << endl; printIntArray (array2); cout << "array2的大小:" << array2.getSize () << endl; cout << "array2的容量:" << array2.getCapacity () << endl; } class Person {public : Person () {} Person (string name, int age) { this ->m_Name = name; this ->m_Age = age; } public : string m_Name; int m_Age; }; void printPersonArray (MyArray<Person>& personArr) { for (int i = 0 ; i < personArr.getSize (); i++) { cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl; } } void test02 () { MyArray<Person> pArray (10 ) ; Person p1 ("孙悟空" , 30 ) ; Person p2 ("韩信" , 20 ) ; Person p3 ("妲己" , 18 ) ; Person p4 ("王昭君" , 15 ) ; Person p5 ("赵云" , 24 ) ; pArray.Push_back (p1); pArray.Push_back (p2); pArray.Push_back (p3); pArray.Push_back (p4); pArray.Push_back (p5); printPersonArray (pArray); cout << "pArray的大小:" << pArray.getSize () << endl; cout << "pArray的容量:" << pArray.getCapacity () << endl; } int main () { test02 (); system ("pause" ); return 0 ; }
总结:
能够利用所学知识点实现通用的数组