本文主要从下面几个方面分析
目录
1: 类模板
2:类模板与函数模板的创建时机
3:类模板中成员函数的创建时机
4:类模板对象做函数参数
5:类模板与继承
6:类模板成员函数类外实现
7:类模板分文件编写
8:类模板与友元
9:类模板案列:数组类封装
1:类模板
作用:建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代表
语法:template<typename T>
解释:
template -----声明创建模板
typename ----表明后面的符号是一种数据类型,可以用class 代替
T --------通用的数据类型,名称可以替换,通常为大写字母
案列
案列描述:我们创建一个 Person类模板,含有成员变量 m_Age,m_Name。我们想让这两个成员变量的类型用一个虚拟的类型来代表,同时这个两个变量的类型可能会不一样。
解决方案:
1:创建一个类模板,由于存在两种数据类型,所以在声明创建模板时应指定两种数据类型,我们这里指定为 NameType和AgeType ,因此整体语句为: template<class NameType, calss AgeType>。
2: 在使用类模板实例化对象时,需要显示地指定数据类型(尖括号<> 就是模板的参数列表)。
代码
#include<iostream>
using namespace std;
template<class NameType,class AgeType>
class Person
{
public:
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void ShowPerson() {
cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}
public:
NameType m_Name;
AgeType m_Age;
};
void test() {
Person<string, int> p("zhangsan", 1000);
p.ShowPerson();
}
int main(void) {
test();
}
// 打印结果
name: zhangsan age: 1000
从 运行的结果知:函数模板正确实例化出对象并调用了相应的成员函数
2:类模板和函数模板区别
类模板和函数模板主要有两点区别
1:类模板没有自动类型推导的使用方式
在案例中:在上面Person类模板中,如果我们隐式的初始化一个对象(Person P ("zhangsan",1000)) 不指定模板参数列表会报错提示:缺少类模板的参数列表。
2:如果想要隐式的初始化对象,那么我们可以为:AgeType指定默认数据类型 int , 为 NameType指定默认数据类型 String, 但是尖括号<> 还是需要保留。
#include<iostream>
using namespace std;
template<class NameType = string,class AgeType = int>
class Person
{
public:
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void ShowPerson() {
cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}
public:
NameType m_Name;
AgeType m_Age;
};
void test() {
Person<> p("zhangsan", 1000);
p.ShowPerson();
}
int main(void) {
test();
}
// 打印结果
name: zhangsan age: 1000
从打印结果可以看出,省略了模板参数列表中的内容,仍然可以实例化对象。
3:类模板中成员函数的创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的
1: 普通类中的成员函数一开始就是可以创建
2:类模板中的成员函数在调用时才创建
案例:
创建两个普通类 Person1和Person2 ,他们各自有一个成员函数showPerson1和showPerson2,接着创建一个类模板 Myclas,含有一个成员变量obj,还含有两个成员函数func1和func2,分别实现调用obj的showPerson1和showPerson2成员函数。
#include<iostream>
#include<string>
using namespace std;
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 func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
int main() {
MyClass<Person1> m1;
m1.func1();
MyClass<Person2> m2;
m2.func2();
return 0;
}
// 打印结果
Person1 show
Person2 show
4:模板对象做函数参数
类模板实例化出的对象,向函数传参一共有三种方式
1:指定传入的类型-----直接显示对象的数据类型
2:参数模板化 ------将对象中的参数变为模板进行传递
3:整个类模板化----将这个对象类型模板化进行传递
案例:创建一个类模板 Person ,模板参数列表指定为 T1和T2,然后实例化出对象,并通过3中不同的方式向函数传参。
#include<iostream>
#include<string>
#include<typeinfo>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age) :m_Name(name), m_Age(age) {};
void showPerson() {
cout << "name = " << this->m_Name << " age = " << this->m_Age << endl;
}
T1 m_Name;
T2 m_Age;
private:
};
// 1: 指定传入的类型
void printPerson1(Person<string, int>& p) {
p.showPerson();
}
// 2: 参数模板化(类模板+函数模板)
template<typename T1,typename T2>
void printPerson2(Person<T1, T2>& p) {
p.showPerson();
cout << "T1 type:" << typeid(T1).name() << endl;
cout << "T2 type:" << typeid(T2).name() << endl;
}
// 3: 整个类模板化(类模板+函数模板)
template<class T>
void printPerson3(T& p) {
p.showPerson();
}
int main() {
Person<string, int> person1("wukong", 100);
printPerson1(person1);
Person<string, int> person2("bajie", 90);
printPerson2(person2);
Person<string, int> person3("shaseng", 30);
printPerson3(person3);
return 0;
}
// 打印
name = wukong age = 100
name = bajie age = 90
name = shaseng age = 30
5:类模板与继承
当类模板碰到继承时,需要注意以下几点。
1:当子类继承的父类是一个类模板时,子类在声明的时候,需要指定父类中的 T类型
2:如果不指定,编译器无法给子类分配内存
3:如果想灵活指定出父类中 T的类型,子类也需要变为类模板
案例
创建一个类模板 Base ,模板的数据类型是 T。然后创建另外一个类 Son1 以指定具体模板数据类型的方式去继承这个类模板,创建另外一个类 Son2 以灵活指定出父类中T 的类型的方法区继承这个类模板。
#include<iostream>
#include<string>
#include<typeinfo>
using namespace std;
template<class T>
class Base {
public:
T m;
};
// 继承时,必须知道父类中 T的类型才可以向下继承
class Son1 :public Base<int> {
public:
Son1() {
cout << typeid(m).name() << endl;
}
};
// 如果想保留父类中 T的类型,子类也需要变为类模板
// 这里子类模板没有 T类型数据
template<class T1>
class Son2 :public Base<T1> {
public:
Son2() {
cout << typeid(T1).name() << endl;
}
};
// 这里子类模板具有T2数据类型,T3继承自父类模板 Base
template<class T2,class T3>
class Son3 :public Base<T3> {
public:
Son3() {
cout << typeid(T2).name() << " " << typeid(T3).name() << endl;
}
};
int main() {
Son1 c1;
Son2<int> c2;
Son3<int, char> c3;
return 0;
}
// 打印结果
int
int
int char
6 : 类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表。
案例:创建一个类模板Person 时,在类模板内部只给出成员函数的声明,在类外实现成员函数的定义。
#include<iostream>
#include<string>
using namespace std;
template<typename T1,typename 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 << "person name: " << this->m_Name << " person age: " << this->m_Age << endl;
}
int main() {
Person<string, int> p("tom", 20);
p.showPerson();
return 0;
}
// 打印结果
person name: tom person age: 20
7:类模板分文件编写
类模板成员函数分文件编写产生的问题以及解决方式
问题:
类模板中成员函数创建时机是在调用阶段,这导致文件编译时链接不到
解决方案:
1:直接包含.cpp 源文件
2: 将声明和定义写在同一个文件中。
案例: 先定义普通类(非模板类):我们可以看到:程序可以正常运行(这也说明了 普通类的成员函数开始就会创建,所以编译器在链接阶段会根据Person.h 文件找到成员函数的定义)
// Person.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string name, int age);
void showPerson();
string m_Name;
int m_Age;
};
// Person.cpp
#include "Person.h"
Person::Person(string name, int age) {
this->m_Name = name;
this->m_Age = age;
}
void Person::showPerson() {
cout << "person name: " << this->m_Name << " person age: " << this->m_Age << endl;
}
// main.cpp
#include"Person.h"
int main() {
Person person("mali", 18);
person.showPerson();
}
// 打印结果
person name: mali person age: 18
在看下如果是声明的类模板函数
// Person.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
template<typename T1,typename T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
// Person.cpp
#include "Person.h"
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 << "person name: " << this->m_Name << " person age: " << this->m_Age << endl;
}
// main.cpp
#include"Person.h"
int main() {
Person<string,int> person("mali", 18);
person.showPerson();
}
// 打印结果
1>D:\C++\ProjectCPlus\Debug\MainDemo5.exe : fatal error LNK1120: 2 个无法解析的外部命令
1>已完成生成项目“MainDemo5.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
运行,可以看到链接过程会出错,提示无法解析这些成员函数。这是因为主函数只包含了person.h,而person.h中只有类模板成员函数的声明而没有定义。同时,类模板的成员函数一开始是不会创建的,所以主函数在包含person.h的时候,不会生成这两个成员函数。这也导致了编译器看不到person.cpp中对函数模板成员函数的定义,所以在链接阶段找不到这两个成员函数的定义。
解决方案1: main.cpp 直接包含 .cpp 源文件 ,并且 Person.cpp 文件添加 #pragma once
// Person.cpp
#pragma once
#include "Person.h"
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 << "person name: " << this->m_Name << " person age: " << this->m_Age << endl;
}
// main.cpp
#include"Person.cpp"
#include"Person.cpp"
int main() {
Person<string,int> person("mali", 18);
person.showPerson();
}
// 打印结果
person name: mali person age: 18
8:类模板与友元
全局函数做有元的类内外实现
9:类模板案例:数组类封装
案例描述
实现一个通用的数组类,要求如下:
1:可以对内置数据类型以及自定义数据类型的数据进行存储
2:将数组中的数据存储到堆区
3:构造函数中可以传入素组的容量
4:提供对应的拷贝构造函数以及operator 防止浅拷贝问题
5:提供尾插法和尾删法对数组中的数据进行增加和删除
6:可以通过下标的方式访问数组中的元素
7:可以获取数组中当前元素个数和数组的容量
参考链接