C++模板是泛型编程的基础,在C++中模板包括:
类模板
定义
template <class T>
类声明和定义
简单使用
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;
}
nameType mName;
ageType mAge;
};
void main()
{
Person<string, string> person("Angel", "999");
person.showPerson();
}
类模板和函数模板的语法是类似的,更多使用可参考函数模板的内容。
类模板和函数模板的区别
- 类模板不能使用自动类型推导的调用方式
- 类模板的模板参数列表可以有默认类型参数
示例:
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;
}
nameType mName;
ageType mAge;
};
void main()
{
//Person person("Angel", "999"); //错误的
Person<string> p1("ai", 10); //第二个模板参数使用默认数据类型
p1.showPerson();
Person<string,string> p2("ai", "十岁"); //第二个模板参数使用显式指定类型参数
p2.showPerson();
}
类模板成员函数创建时机
- 类模板的成员函数只有程序中使用到,编译器才会真正创建相应的函数
示例:
class myShow1
{
public:
void show1()
{
cout << "show1" <<endl;
}
};
class myShow2
{
public:
void show2()
{
cout << "show2" <<endl;
}
};
template <class T>
class MyClass
{
T obj;
public:
//成员函数
void fun1()
{
obj.show1();
}
void fun2()
{
obj.show2();
}
};
void main()
{
MyClass<myShow1> myclass;
myclass.fun1();
//myclass.fun2(); //使用到show2,编译根据指定的类型去创建该函数,此时则会报错“show2”: 不是“myShow1”的成员
//屏蔽此处调用则程序正常,即不使用,成员函数就不创建
}
类模板对象做函数参数
类模板实例化出的对象,向函数传参的方式:
- 指定传入的类型 — 直接显式指定对象的数据类型
- 参数模板化 — 将对象中的参数变为模板进行传递
- 整个类模板化 — 将这个对象类型 模板化进行传递
示例:
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
m_Name = name;
m_Age = age;
}
void showPerson()
{
cout << "name = " << m_Name << " age = " << m_age << endl;
}
T1 m_Name;
T2 m_Age;
};
//1、指定传入的类型
void showPerson1(Person<string, int> &person)
{
person.showPerson();
}
//2、参数模板化,实际就是函数模板和类模板结合使用
template <class T1, class T2>
void showPerson2(Person<T1, T2> &person)
{
cout << typeid(T1).name() << endl; // 可以查看传入的类型名称
cout << typeid(T2).name() << endl;
person.showPerson();
}
//3、整个类模板化,实际就是函数模板和类模板结合使用
template <class T>
void showPerson3(T &person)
{
cout << typeid(T).name() << endl;
person.showPerson();
}
void main()
{
Person<string, int> person("哈哈哈", 456);
showPerson1(person);
showPerson2(person);
showPerson3(person);
}
结论:使用的比较广泛的是第一种,直接指定传入的参数类型。
类模板与继承
- 继承时直接指定父类(一般不常用,父类则类型指定,则失去模板的意义)
- 子类也写为类模板,则可以灵活指定父类模板参数类型
template <class T>
class Base
{
T m_obj;
};
//1、直接指定父类模板参数列表类型
class Son : public Base<int>
{
};
//2、子类也为类模板
template <class T1, class T2>
class Son2 : public Base<T2>
{
public:
Son2()
{
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
}
};
void main()
{
Son2<string,int> son2;
system("pause");
}
类模板成员函数的类外实现
- 类模板成员函数类外实现需要加上类模板的参数列表,这样编译器才知道函数是类模板成员函数的实现
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
void ShowPerson();
private:
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2, class T3>
class Person1
{
public:
Person1(T1 name, T2 age);
void Person(T1 name, T2 age);
void ShowPerson();
private:
T1 m_Name;
T2 m_Age;
T3 m_Like;
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
m_Name = name;
m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::ShowPerson() // 如果不加类模板的参数列表<T1,T2>,则编译器不知道是对person还是person1成员函数的实现
{
cout << "name = "<< m_Name << endl;
cout << "age = " << m_Age << endl;
}
void main()
{
Person<string, int> p1("名字", 10);
p1.ShowPerson();
system("pause");
}
类模板的分文件编写
像普通类分开.h和.cpp分开实现,调用.h使用,调用类模板成员函数编译会提示无法无法解析的外部命令
问题导致原因:主要是C++的分文件编译导致类模板的成员函数没有创建,具体分析参考https://blog.csdn.net/qq_41929943/article/details/103004083
解决方法
- 直接包含.cpp文件使用(不常用);
- 将类模板的声明和实现写到一个文件,文件命名为.hpp(约定俗成的后缀);
类模板与友元函数
- 全局函数类内实现 ---- 建议使用
- 全局函数类外实现 ---- 比较复杂,不是必要的话不建议使用
//实现全局函数
template<class T1, class T2> // 因为要用到类模板,所以该函数写成模板函数
void showPerson2(Person<T1, T2> person)
{
cout << "name = " << person.m_Name << endl; // 访问类的私用成员
cout << "age = " << person.m_Age << endl;
}
//实现全局函数
template<class T1, class T2,class T3> // 因为要用到类模板,所以该函数写成模板函数
void showPerson2(Person<T1, T2> person)
{
cout << "name1 = " << person.m_Name << endl; // 访问类的私用成员
cout << "age1 = " << person.m_Age << endl;
}
template<class T1, class T2>
class Person
{
//类内实现友元函数,可以访问类的私有成员---比较简单
friend void showPerson1(Person<T1, T2> person)
{
cout << "name = " << person.m_Name << endl;
cout << "age = " << person.m_Age << endl;
}
//类外实现的函数要能放访问类的私有成员,需要声明为类的友元
//friend void showPerson2(Person<T1, T2> person); //该写法是错的
template <class T1,class T2,class T3>
friend void showPerson2<T1,T2,T3>(Person<T1, T2> person); //需要通过showPerson2<>让编译器指定声明为友元的是个模板函数,如果友元函数发生重载,还需要指定类模板参数类别来区别重载的函数
public:
Person(T1 name, T2 age)
{
m_Name = name;
m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void main()
{
Person<string, int> p("小喵", 100);
showPerson1(p);
showPerson2<string,int,int>(p);
system("pause");
}