C++类模板
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
`关于类模板的一些问题
一、类模板的基本用法
类模板:创建一个通用的类,类中成员的类型未指定是哪一种具体类型,用一个虚拟的类型来代表
格式:
template <typename T>
类
提示:
template是声明模板的创建
typename也可用class代替,两者没区别
T是通用的数据类型,也可用其他字母代替,一般是大写
示例:
//类模板
template<typename T1, typename T2>
class Student
{
public:
Student(T1 name, T2 id)
{
this->m_name = name;
this->m_id = id;
}
void show()
{
cout << "name: " << this->m_name << "id: " << this->m_id << endl;
}
T1 m_name;
T2 m_id;
};
void test()
{
Student<string, int> p("张三", 18);
p.show();
}
int main()
{
test();
system("pause");
return 0;
}
二、类模板与函数模板的区别
类模板与函数模板的区别:
1.类模板没有自动类型推导的使用方式
2.类模板在模板参数列表中可以有默认参数
3.类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型
示例:
template<typename T1, typename T2 = int> //缺省
class Student
{
public:
Student(T1 name, T2 id)
{
this->m_name = name;
this->m_id = id;
}
void show()
{
cout << "name: " << this->m_name << "id: " << this->m_id << endl;
}
T1 m_name;
T2 m_id;
};
void test1()
{
Student<string, int> p("张三", 18);//必须显式类型推导
p.show();
}
void test2()
{
Student<string> p("李四",20);//类模板参数列表已经进行缺省,故只写了string就可以
}
int main()
{
test1();
system("pause");
return 0;
}
三、类模板中成员函数创建时机
类模板中成员函数与普通类中成员函数的创建时机是不同的:
——普通类的成员函数在编译时就已经创建
——类模板的成员函数在调用时才创建
示例:
#include <iostream>
using namespace std;
class person1
{
public:
void show1()
{
cout <<"我是person1"<<endl;
}
};
class person2
{
void show2()
{
cout <<"我是person2"<<endl;
}
};
template <class T>
class usePerson
{
public:
T per;
//类模板中的成员函数,不是一开始就创建了,类模板调用时候创建
void fun1()
{
per.show1();
}
void fun2()
{
per.show2();
}
};
四、类模板中成员函数在类外如何实现
类模板的成员函数在类内声明,在类外如何实现的呢?
示例:
template <class T1,class T2>
class MM
{
public:
MM(T1 one,T2 two) :one(one),two(two){}
void print(); //函数类内声明
protected:
T1 one;
T2 two;
};
template <class T1,class T2> //注意:这里也要写明template
void MM<T1,T2>::print() //这里不仅要写明作用域,还要写<T1,T2>
{
cout << one << endl;
cout << two << endl;
}
五、类模板对象作为函数参数的实现
三种传入方式:
1.指定传入类型
2.参数模板化
3.整个类进行模板化
示例:
#include<iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age)
{
this->m_name = name;
this->m_age = age;
}
void showPerson()
{
cout << "姓名: " << m_name << " " << "年龄: " << m_age << endl;
}
public:
T1 m_name;
T2 m_age;
};
//1.指定传入类型
void printPerson01(Person<string,int>& p)
{
p.showPerosn();
}
void test01()
{
Person<string,int>p("张三",23);
//p.showPerson();
printPerson01(p);
}
//2.参数模板化
template<class T1,class T2>
void printPerson02(Person<T1,T2>&P)
{
p.showPerson();
}
void test02()
{
Person<string,int>p("李四",34);
printPerson02(p);
}
//3.整个类进行模板化
template<class T>
void printPerson03(T & p)
{
p.showPerson();
}
void test03()
{
Person<string,int>p("王五",56);
printPerson03(p);
}
int main()
{
//test01();//用哪个时开哪个(因为我起的对象名都是p)
//test02();
test03();
system("pause");
system("cls");
}
六、类模板继承
1.普通子类继承类模板: 格式:class 子类:public 父类<指定类型> //这里指定类型,必须告诉Base中T的类型,否则T无法分配内存
2.类模板继承类模板 格式: template<class T1,class T2>
class son: public person<T1,T2> {}
示例:
//类模板与继承
template<class T>
class Parent
{
T m;
};
//class Son: public Parent//错误,必须要知道父类中的T类型,才能继承给子类
class Son :public Parent<int>
{
};
void test01()
{
Son s1;
}
//如果想灵活指定父类中T的类型,子类也需要变成类模板
template<class T1,class T2>
class Son2 : public Parent<T2>
{
public:
Son2()
{
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test02()
{
Son2<int,char> s2;
}
int main()
{
test02();
system("pause");
return 0;
}
七、类模板分文件编写
解决方案:
1.当类模板的声明与实现分开始,main.cpp中需要包含(include) .cpp文件
2.也可以把声明和实现写到一个文件中,此文件后缀名为 .hpp
八、类模板与友元
有两种实现方式:
1.类内实现友元函数(直接类内声明即可)
2.类外实现友元函数(需要提前让编译器知道全局函数的存在)
示例:1.类内实现
template<class T1, class T2>
class Person
{
//全局函数类内实现
friend void printPerson(Person<T1, T2> p)
{
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
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", 30);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}
2.类外实现
//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;
//类外实现
template<class T1, class T2>
void printPerson(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);
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", 30);
printPerson(p);
}
int main()
{
test01();
system("pause");
return 0;
}
九、类模板的特化
类模板的特化:
1.完全特化
2.局部特化
template <class T1,class T2,class T3>
class Data
{
public:
Data(T1 one,T2 two,T3 three) : one(one),two(two),three(three)
{
cout << "原生模板" << endl;
}
protected:
T1 one;
T2 two;
T3 three;
};
//完全特化:
template<>
class Data<int,string,string>
{
public:
Data(int one,string two,string three) : one(one),two(two),three(three)
{
cout << "完全特化" << endl;
}
protected:
int one;
string two;
string three;
};
//局部特化:
template <class T1>
class Data<T1,T1,T1>
{
public:
Data(T1 one,T1 two,T1 three) : one(one),two(two),three(three)
{
cout << "局部特化" << endl;
}
protected:
T1 one;
T2 two;
T3 three;
};
调用时:
Data<int,string,string> data1(1,"张三","李四"); //肯定调用的是完全特化
Data<string,int,string> data2("王五",5,"李四"); //在特化中没找到与之匹配的模板,肯定调用的是原生模板
Data<string,string,string> data3("王五","张三","李四"); //肯定调用的是局部特化
十、关于类模板需要注意的一些点
1.类模板在调用时,必须显式调用,不能隐式调用(即类模板必须显式实例化)
2.类模板没有自动类型推导的使用方式
3.类模板不是一个完整类,所以在用到类名的地方,必须要用类名<类型>方式去使用 (需要时刻注意)
4.类模板也能传入常量 如: template <class T1,int size> class array1 { … };
注:本文参考b站C++网课视频