1.3.1 类模板语法
类模板作用:
-
建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表
语法:
template类
解释:
template ··· 声明创建模板
typename ··· 表面其后面的符号是一种数据类型,可以用class代替
T ··· 通用的数据类型,名称可以替换,通常为大写字母
示例:
#includeusing namespace std; #include //类模板 template class Person { public: Person(NameType name, AgeType age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "name:" << this->m_Name << ",年龄:" << this->m_Age << endl; } NameType m_Name; AgeType m_Age; }; void test01() { Person p1("孙悟空",190); p1.showPerson(); } int main() { test01(); system("pause"); return 0; }
运行效果如图
总结:类模板与函数模板语法相似,在声明模板template后面加类 , 此类称为类模板
1.3.2 类模板与函数模板区别类模板与函数模板区别主要有两点:
-
类模板没有自动类型推导的使用方式
-
类模板在模板参数列表中可以有默认参数
示例:
#includeusing namespace std; #include //类模板与函数模板区别 //template //可以 template class Person { public: Person(NameType name, AgeType age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "姓名:" << this->m_Name<< "年龄" << this->m_Age << endl; } NameType m_Name; AgeType m_Age; }; //1、类模板没有自动类型推导使用方式 void test01() { //Person p("孙悟空",1000);错误,无法用自动类型推导 Person p("孙悟空",1000);//正确,只能用显示指定类型 p.showPerson(); } //2、类模板在模板参数列表中可以有默认参数 void test02() { Person p("猪八戒", 999); p.showPerson(); } int main() { //test01(); test02(); system("pause"); return 0; }
运行结果如图:
总结:
-
类模板使用只能用显示指定类型方式
-
类模板中的模板参数列表可以有默认参数
类模板中成员函数和普通类成员函数创建时机是有区别的:
-
普通类中的成员函数一开始就可以创建
-
类模板中的成员函数在调用时才创建
示例:
#includeusing namespace std; //类模板中成员函数创建时机 //类模板中成员函数在调用时才去创建 class Person1 { public: void showPerson1() { cout << "Person1 show" << endl; } }; class Person2 { public: void showPerson2() { cout << "Person2 show" << endl; } }; template class MyClass { public: T obj; //类模板中的成员函数 void func1() { obj.showPerson1(); } void func2() { obj.showPerson2(); } }; void test01() { MyClass m; //m.func1();//编译会出错,说明函数调用才会创建成员函数 m.func2(); } int main() { test01(); system("pause"); return 0; }
运行效果如图
总结:类模板中的成员并不是一开始就创建的,在调用是才去创建
1.3.4 类模板对象的函数参数学习目标:
-
类模板实例化出的对象,向函数传参的方式
一共有三种传入方式
-
指定传入类型 ···直接显示对象的数据类型
-
参数模板化 ···将对象中的参数变为模板进行传递
-
整个类模板化 ···将这个对象类型模板化进行传递
示例:
#includeusing namespace std; #include //类模板对象做函数参数 template class Person { public: Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "姓名" << this->m_Name << " ,年龄" << this->m_Age << endl; } T1 m_Name; T2 m_Age; }; //1、指定传入类型//最常用 void printPerson1(Person &p) { p.showPerson(); } void test01() { Person p("孙悟空",100); printPerson1(p); } //2、参数模板化 template void printPerson2(Person &p) { p.showPerson(); cout << "T1的数据类型为" << typeid(T1).name() << endl; cout << "T2的数据类型为" << typeid(T2).name() << endl; } void test02() { Person p("猪不戒", 90); printPerson2(p); } //3、整个类模板化 template void printPerson3(T &p) { p.showPerson(); cout << "T的数据类型为" << typeid(T).name() << endl; } void test03() { Person p("唐僧", 30); printPerson3(p); } int main() { test01(); test02(); test03(); system("pause"); return 0; }
运行效果如图:
总结:
-
通过类模板创建的对象,可以有三种方向函数重进行传参
-
使用比较广泛是第一种,指定传入的类型
当类模板平壤到继承时,需要注意以下几点
-
当子类继承的父类是一个类模板时,子类在声明的时候,要制定出父类中T的类型
-
如果不指定,编译器无法给子类分配内存
-
如果想灵活指定出父类中的类型,子类也需变为类模板
示例:
#includeusing namespace std; //类模板与继承 template class Base { T m; }; //class Son :public Base //错误,必须要知道父类中的T类型,才能继承给子类 class Son:public Base { }; void test01() { Son s1; } //如果想灵活指定父类中T类型,子类也需要变为类模板 template class Son2 :public Base { public: Son2() { cout << "T1的类型为:" << typeid(T1).name() << endl; cout << "T2的类型为:" << typeid(T2).name() << endl; } T1 obj; }; void test02() { Son2 S2; } int main() { test01(); test02(); system("pause"); return 0; }
运行结果如图:
总结:如果父类是类模板,子类需要指定出父类中T的数据类型
1.3.6 类模板成员函数类外实现学习目标:能够掌握类模板中的成员函数类外实现
示例:
#includeusing namespace std; #include //类模板成员函数类外实现 template class Person { public: Person(T1 name, T2 age); // { // this->m_Name = name; // this->m_Age = age; // } void showPerson(); // { // cout << "姓名" << this->m_Name << " ,年龄" << this->m_Age << endl; // } T1 m_Name; T2 m_Age; }; //构造函数的内外实现 template Person ::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } //成员函数的类外实现 template void Person ::showPerson() { cout << "姓名" << this->m_Name << " ,年龄" << this->m_Age << endl; } void test01() { Person P("Tom", 20); P.showPerson(); } int main() { test01(); system("pause"); return 0; }
运行截图
总结:类模板中成员函数类外实现时,需要加上模板参数列表
1.3.7 类模板份文件编写学习目标
-
掌握类模板成员函数分文件编写产生的问题以及解决方式
问题:
-
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
-
解决方式1:直接包含.cpp文件
-
解决方式2:将声明和实现写道同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
示例:
person.hpp中代码:
#pragma once #includeusing namespace std; #include //类模板分文件编写问题以及解决 template class Person { public: //构造函数 Person(T1 name, T2 age); void showPerson(); T1 m_Name; T2 m_Age; }; template Person ::Person(T1 name, T2 age) { this->m_Age = age; this->m_Name = name; } template void Person ::showPerson() { cout << "姓名:" << this->m_Name << "年龄" << this->m_Age << endl; }
类模板分文件编写.cpp中代码
#includeusing namespace std; //第一种解决方式,直接包含源文件 //#include "Person.cpp" //第二种解决方式,将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件 #include "Person.hpp" // #include // //类模板分文件编写问题以及解决 // template // class Person // { // public: // //构造函数 // Person(T1 name, T2 age); // // void showPerson(); // T1 m_Name; // T2 m_Age; // }; // template // Person ::Person(T1 name, T2 age) // { // this->m_Age = age; // this->m_Name = name; // } // // template // void Person ::showPerson() // { // cout << "姓名:" << this->m_Name << "年龄" << this->m_Age << endl; // } void test01() { Person p("Tom", 17); p.showPerson(); } int main() { test01(); system("pause"); return 0; }
运行结果如图
总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp
1.3.8 类模板与友元学习目标:
-
掌握类模板配合友元函数的类内和类外实现
全局函数类内实现--直接在类内声明友元即可
全局函数类外实现--需要提前让编译器知道全局函数的存在
示例:
#includeusing namespace std; #include //通过全局函数 打印Person信息 //提前让编译器知道Person类存在 template class Person; //类外实现 template void printPerson2(Person p) { cout << "类外实现:---姓名" << p.m_Name << " ,年龄" << p.m_Age << endl; } template class Person { //全局函数 类内实现 friend void printPerson(Person p) { cout << "姓名" << p.m_Name << " ,年龄" << p.m_Age << endl; } //全局函数类外实现 //加空模板的参数列表 //如果全局函数 是类外实现,需要让编译器提前知道这个函数的存在 friend void printPerson2<>(Person p); public: Person(T1 name, T2 age) { this->m_Age = age; this->m_Name = name; } private: T1 m_Name; T2 m_Age; }; //1、全局函数在类内实现 void test01() { Person p("tom", 18); printPerson(p); } //全局函数在类外实现 void test02() { Person p("jetty", 18); printPerson2(p); } int main() { //test01(); test02(); system("pause"); return 0; }
运行效果:
总结:建议全局函数做雷内实现,用法简单,而且编译器可以直接识别。
1.3.9 类模板案例案例描述:实现一个通用的数组类,要求如下:
-
可以对内置数据类型以及自定义数据类型的数据进行存储
-
将数组中的数据存储到堆区
-
构造函数中可以传入数组的容量
-
提供对应的拷贝构造函数以及operator = 防止浅拷贝问题
-
提供尾插法和尾删法对数组中的数据进行增加和删除
-
可以通过下标的方式访问数组中的元素
-
可以获取数组中当前元素个数和数组的容量
示例:
myArray.h中的代码
//自己的通用的数组类 #pragma once #includeusing namespace std; template class MyArray { public: //有参构造 参数 容量 MyArray(int capacity) { //cout << "Myarray有参构造调用" << endl; this->m_Capacity = capacity; this->m_Size = 0; this->pAddress = new T[this->m_Capacity]; } //拷贝构造 MyArray(const MyArray & arr) { //cout << "Myarray拷贝构造调用" << endl; this->m_Capacity = arr.m_Capacity; this->m_Size = arr.m_Size; //this->pAddress = arr.pAddress;//浅拷贝 //深拷贝 this->pAddress = new T[this->m_Capacity]; //将arr中的数据拷贝都过来 for (int i = 0; i < this->m_Size; i++) { //如果T为对象,而且还包含指针,必需要重载 = *** 作符 ,因为这个等号不是构造,而是赋值 //普通类可以直接 = 但是指针类型需要深拷贝 this->pAddress[i] = arr.pAddress[i]; } } //重载 = *** 作符 operator= 防止浅拷贝问题 MyArray& operator=(const MyArray &myarray) { //cout << "Myarray的operator=调用" << endl; //先判断原来堆区是否有数据,如果有先释放 if (this->pAddress!=NULL) { delete[]this->pAddress; this->pAddress = NULL; 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 m_Size;i++) { this->pAddress[i] = myarray[i]; } return *this; } //尾插法 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--; } //通过下标方式访问数组中的元素 //重载[] *** 作符 arr[0] T & operator[](int index) { return this->pAddress[index];//不用考虑越界,用户自己处理 } //返回数组容量 int getCapacity() { return this->m_Capacity; } //返回数组大小 int getSize() { return this->m_Size; } //析构函数 ~MyArray() { if (this->pAddress != NULL) { //cout << "Myarray析构函数调用" << endl; delete[] this->pAddress; this->pAddress = NULL; this->m_Capacity = 0; this->m_Size = 0; } } private: T * pAddress;//指针指向堆区开辟的真实数组 int m_Capacity;//数组容量 int m_Size;//数组大小 };
类模板案例一数组类组装封装.cpp中
#includeusing namespace std; #include "MyArray.hpp" #include void printIntArray(MyArray &arr) { for (int i=0;iarr1(5); for(int i=0;i<5;i++) { //利用尾插法向数组中插入数据 arr1.Push_Back(i); } cout << "arr1的打印输出为" << endl; printIntArray(arr1); cout << "arr1的容量为" << arr1.getCapacity() << endl; cout << "arr1的大小为:" << arr1.getSize() << endl; MyArray arr2(arr1); cout << "arr2的打印输出为" << endl; printIntArray(arr2); //尾删 arr2.Pop_Back(); cout << "arr2尾删后:" << endl; cout << "arr2的容量为:" << arr2.getCapacity() << endl; cout << "arr2的大小为:" << arr2.getSize() << endl; // MyArray arr3(100); // arr3 = arr1; } //测试自定义数据类型 class Person { public: Person(){} Person(string name, int age) { this->m_Name = name; this->m_Age = age; } string m_Name; int m_Age; }; void printPersonArray(MyArray & arr) { for (int i=0;i arr(10); Person p1("孙悟空",999); Person p2("孙策",28); Person p3("虞姬",18); Person p4("鲁班",5); Person p5("典韦",10); //将数据插入到数组中 arr.Push_Back(p1); arr.Push_Back(p2); arr.Push_Back(p3); arr.Push_Back(p4); arr.Push_Back(p5); //打印数组 printPersonArray(arr); //输出容量 cout << "arr的容量为" << arr.getCapacity() << endl; //输出大小 cout << "arr的大小为:" << arr.getSize() << endl; } int main() { test01(); test02(); system("pause"); return 0; }
测试结果图
总结:
能够利用所学知识点实现通用的数组。
欢迎大家与我私信交流欢迎分享,转载请注明来源:内存溢出
评论列表(0条)