C++另一种编程思想泛型编程;
C++提供两种模板机制:
函数模板和类模板;
函数模板的作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代替;
语法:
template
函数
template–声明创建模板;
T–通用的数据类型,名称可以替换,通常为大写字母;
模板使用是可以有两种使用方法:
自动类型推导(所使用的的数据类型必须要一致);
显示指定类型;
#include <iostream>
using namespace std;
template<typename T> //声明一个模板,typename可以替换成class
void swapdata(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
double c = 0.2;
double d = 0.4;
//自动类型推导方式使用模板
swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
//显示指定类型
swap<double>(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
system("pause");
return 0;
}
数组排序:
#include <iostream>
using namespace std;
template<class T>
void myswap(T&a, T&b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template<class T>
void mysort(T a[],int num)
{
int i = 0;
int j = 0;
for (i = 0; i < num; i++) { //选择排序法
int Max = i;
for (j = i + 1; j < num;j++ ) {
if (a[j] > a[Max]) {
Max = j;
}
}
if (Max != i) {
//交换max和i元素
myswap(a[Max], a[i]);
}
}
}
template<class T>
void print(T a[], int len) {
int i = 0;
for (i = 0; i < len; i++) {
cout << a[i] << " ";
}
cout << endl;
}
void test01()
{
//字符数组测试
char c[] = "abcdef";
int size = sizeof(c) / sizeof(char);
mysort(c, size);
print(c, size);
}
void test02()
{
//整数数组测试
int arr[] = { 2,1,4,5,3,6,7,9,8,0 };
int size = sizeof(arr) / sizeof(int);
mysort(arr, 10);
print(arr, size);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
普通函数和函数模板的区别:
普通函数调用时可以发生自动类型转换(隐式类型转换);
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换(例如普通函数在做int+char时会将char转换为ASCII码);
如果利用显示指定类型的方式,可以发生隐式类型转换;
普通函数和函数模板调用规则:
调用规则如下:
1.如果函数模板和普通函数都可以实现,优先调用普通函数;
2.可以通过空模板参数列表来强制调用函数模板;
3.函数模板也可以发生重载;
4.如果函数模板可以产生更好的匹配,优先调用函数模板;
但是使用了普通函数就不要使用函数模板,避免出现二义性;
#include <iostream>
using namespace std;
void fun(int a,int b) {
cout << "调用普通函数" << endl;
}
template<class T>
void fun(T a,T b) {
cout << "调用模板" << endl;
}
template<class T>
void fun(T a, T b ,T c) {
cout << "调用重载的模板" << endl;
}
void test()
{
int a = 10;
int b = 20;
//fun(a, b); //优先调用的是普通函数
//通过空模板参数列表,强制调用函数模板
//fun<>(a, b);
//调用重载模板
//fun(a,b,100);
//函数模板产生更好的匹配(虽然可以使用普通函数对其进行转换,但是编译器认为模板更为匹配)
char c = 'c';
char d = 'd';
fun(c, d);
}
int main()
{
test();
system("pause");
return 0;
}
模板的局限性:
有些特定的数据类型需要具体化的方式做特殊实现;
#include<iostream>
#include <string>
using namespace std;
class person {
public:
int m_age;
string m_name;
};
template <class T>
bool myCompare(T &a, T &b) {
if (a == b) {
return true;
}
else {
return false;
}
}
template<> bool myCompare(person &p1, person &p2) //具体化myCompare函数中形参的类型;
{
if (p1.m_age == p2.m_age && p1.m_name == p2.m_name) {
return true;
}
else {
return false;
}
}
void test() {
person p1 = { 10,"ming" };
person p2 = { 20,"ming" };
bool ret = myCompare(p1, p2);
if (ret) {
cout << "一样" << endl;
}
else {
cout << "不一样" << endl;
}
}
int main()
{
test();
system("pause");
return 0;
}
类模板:
作用:建立一个通用类,类中的成员,数据类型可以不具体制定,用一个虚拟类型代表;
语法个模板函数一样;
Template
类
#include<iostream>
#include <string>
using namespace std;
template<class Nametype,class Agetype>
class person {
public:
person(Nametype name, Agetype age) {
this->m_age = age;
this->m_name = name;
}
void showinfo() {
cout << "name is:" << m_name << endl;
cout << "age is:" << m_age << endl;
}
Nametype m_name;
Agetype m_age;
};
void test()
{
person<string,int>p1("cao", 24);
p1.showinfo();
}
int main()
{
test();
system("pause");
return 0;
}
类模板和函数模板区别:
1.类模板没有自动类型推导的使用方式;
2.类模板在模板参数列表中可以有默认参数;
#include<iostream>
#include <string>
using namespace std;
template<class Nametype,class Agetype = int > //默认第二个参数为整型
class person {
public:
person(Nametype name, Agetype age) {
this->m_age = age;
this->m_name = name;
}
void showinfo() {
cout << "name is:" << m_name << endl;
cout << "age is:" << m_age << endl;
}
Nametype m_name;
Agetype m_age;
};
void test()
{
//person p2("cao", 20); 类模板不能自动推导类型
person<string>p1("cao", 24); //因为在创建类模板时将第二个参数设置为默认参数整型,所以不会报错
p1.showinfo();
}
int main()
{
test();
system("pause");
return 0;
}
类模板中成员函数创建时机:
普通类中成员函数一开始就可以创建;
类模板中的成员函数在调用时才创建;
类模板做函数参数:
传入方式:
1.指定传入的类型 --直接显示对象的数据类型(最常用);
2.参数模板化 --将对象中的参数变为模板进行传递;
3.整个类模板化 --将这个对象类型模板化进行传递;
#include <iostream>
#include <string>
using namespace std;
template<class T1,class T2>
class person {
public:
person(T1 name,T2 age) {
this->m_age = age;
this->m_name = name;
}
void showinfo() {
cout << "age is " << this->m_age << " name is " << this->m_name << endl;
}
T1 m_name;
T2 m_age;
};
//指定传入类型
void print01(person<string, int>&p)
{
p.showinfo();
}
void test01()
{
person<string, int>p("caohai", 10);
print01(p);
}
//参数模板化
template<class T1, class T2 >
void print02(person<T1, T2>&p)
{
p.showinfo();
}
void test02()
{
person<string, int>p("cao", 20);
print02(p);
}
//类模板化
template<class T1>
void print03(T1 &p)
{
p.showinfo();
}
void test03()
{
person<string, int>p("hai", 30);
print03(p);
}
int main()
{
test01();
test02();
test03();
system("pause");
return 0;
}
类模板与继承
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;
如果不指定,编译器无法给子类分配内存;
如果想灵活指定出父类中T的类型,子类也需要变为类模板;
#include<iostream>
#include <string>
using namespace std;
template <class T1>
class father {
public:
T1 a;
};
//指定父类T的类型
class son :public father<char>
{
};
void test01()
{
son s1;
}
//灵活指定父类T类型
template<class T1,class T2>
class son2 :public father<T2>
{
public:
son2() {
cout << "T2类型:" << typeid(T1).name() << endl;
cout << "T2类型:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test02()
{
son2<string,int>s; // 此时父类中的T2为int类型
}
int main()
{
test02();
system("pause");
return 0;
}
类模板成员函数类外实现:
#include<iostream>
#include <string>
using namespace std;
template<class T1,class T2>
class father {
public:
father(T1 age, T2 name);
void showinfo();
T1 m_age;
T2 m_name;
};
//构造函数类外实现
template<class T1,class T2>
father<T1, T2>::father(T1 age, T2 name)
{
this->m_age = age;
this->m_name = name;
}
//成员函数类外实现
template<class T1,class T2>
void father<T1, T2>::showinfo()
{
cout << "name:" << this->m_name << " age" << this->m_age<<endl;
}
void test()
{
father<int, string>f(24, "caohai");
f.showinfo();
}
int main()
{
test();
system("pause");
return 0;
}
类模板分文件编写:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到;
解决方式:
1.直接包含.cpp源文件(声明和实现单独写,在主函数源文件中包含函数实现活成的源文件);
2.将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定名称并非强制,在主函数的源文件中包含该.hpp文件;
类模板与友元:
全局函数类内实现–直接在类内声明友元即可;
全局函数类外实现–需要提前让编译器知道全局函数的存在;
#include <iostream>
#include <string>
using namespace std;
//类外实现为了让编译器知道有person类模板
template<class T1, class T2>
class person;
//类外实现
template<class T1,class T2>
void print02(person<T1,T2> p) {
cout << "姓名:" << p.m_name << "年龄:" << p.m_age << endl;
}
template<class T1, class T2>
class person {
//类内实现全局函数
friend void print(person<T1,T2> p)
{
cout << "姓名:" << p.m_name << "年龄:" << p.m_age << endl;
}
//类外实现
friend void print02<>(person<T1,T2> p); //加入<>空模板参数列表
public:
person(T1 name,T2 age) {
this->m_age = age;
this->m_name = name;
}
T1 m_name;
T2 m_age;
};
//类内实现
void test01()
{
person<string, int>p("caohai", 24);
print(p);
}
//类外实现
void test02()
{
person<string, int>p("cao", 20);
print02(p);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
learned from:黑马程序员