所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
(1)为什么要有函数模板
需求:写 n 个函数,交换 char 类型、int 类型、double 类型变量的值。
void myswap_int(int &a, int &b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void myswap_char(char &a, char &b)
{
char tmp;
tmp = a;
a = b;
b = tmp;
}
// 函数模板
template <typename T>
void myswap(T &x, T &y)
{
T tmp;
tmp = x;
x = y;
y = tmp;
}
void main()
{
// 正常编程
{
int a = 10;
int b = 20;
myswap_int(a, b);
cout << "a: " << a << " b: " << b << endl;
char x = ‘a‘;
char y = ‘b‘;
myswap_char(x, y);
cout << "x: " << x << " y: " << y << endl;
}
// 使用函数模板
{
// 1. 显示类型调用
int a = 30;
int b = 40;
myswap<int>(a, b);
cout << "a: " << a << " b: " << b << endl;
// 2. 自动类型推导
char x = ‘c‘;
char y = ‘d‘;
myswap(x, y);
cout << "x: " << x << " y: " << y << endl;
}
system("pause");
}
(2)函数模板做函数参数
实现不同类型的排序:
// 函数模板本质:类型参数化
template <typename T1, typename T2>
void mySort(T1 *arr, T2 len)
{
T1 tmp;
T2 i, j;
for (i = 0; i < len; i++)
{
for (j = i + 1; j < len; j++)
{
if (arr[i] > arr[j])
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
template <typename T1, typename T2>
void myPrint(T1 *arr, T2 len)
{
T2 i;
for (i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void main()
{
// int
int myArr[] = { 10,20,30,40,50,60,33,66,44,32 };
int Arrlen = sizeof(myArr) / sizeof(*myArr);
mySort<int, int>(myArr, Arrlen);
myPrint<int, int>(myArr, Arrlen);
// char
char myStr[] = "gdhientfw563790";
int Strlen = strlen(myStr);
mySort<char, int>(myStr, Strlen);
myPrint<char, int>(myStr, Strlen);
system("pause");
}
(3)函数模板遇上函数重载
函数模板可以像普通函数一样被重载;
C++编译器优先考虑普通函数;
如果函数模板可以产生一个更好的匹配,那么选择模板;
可以通过空模板实参列表的语法限定编译器只通过模板匹配 ;
int Max(int a, int b)
{
cout << "int Max(int a, int b)" << endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout << "T Max(T a, T b)" << endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout << "T Max(T a, T b, T c)" << endl;
return Max(Max(a, b), c);
}
void main()
{
// 当函数模板和普通函数都符合调用时, 优先选择普通函数
cout << Max(1, 2) << endl;
// 若显示使用函数模板, 则使用 <> 类型列表
cout << Max<>(10, 20) << endl;
// 如果函数模板产生更好的匹配 使用函数模板
cout << Max(3.0, 4.0) << endl;
// 重载
cout << Max(5.0, 6.0, 7.0) << endl;
// 调用普通函数
cout << Max(‘a‘, 100) << endl;
}
(4)C++编译器函数模板机制
编译器并不是把函数模板处理成能够处理任意类的函数;
编译器从函数模板通过具体类型产生不同的函数;
编译器会对函数模板进行两次编译:
在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
类模板用于实现类所需数据的类型参数化;
类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响;
(1)单个类模板
类模板定义、使用、做函数参数
// 类模板定义
template <typename T>
class myClass
{
public:
myClass(T a)
{
this->a = a;
}
void myPrint()
{
cout << "a: " << a << endl;
}
private:
T a;
};
// 类模板 做函数参数
void Print(myClass<int> &a)
{
a.myPrint();
}
void Print(myClass<char> &a)
{
a.myPrint();
}
void main()
{
// 类模板使用
myClass<int> a1(10);
myClass<char> a2(‘c‘);
Print(a1);
Print(a2);
}
(2)类模板派生
类模板派生普通类;类模板派生类模板
结论: 子类从模板类继承的时候,需要让编译器知道父类的数据类型是什么(数据类型的本质:固定大小内存块的别名)
// 类模板定义
template <typename T>
class myClass
{
public:
myClass(T t=0)
{
this->t = t;
}
void myPrint()
{
cout << "t: " << t << endl;
}
protected:
T t;
};
// 类模板派生普通类
class A : public myClass<int>
{
public:
A(int a, int t) : myClass(t)
{
this->a = a;
}
void myPrint()
{
cout << "a: " << a << " t: " << t << endl;
}
private:
int a;
};
// 类模板派生模板类
template <typename T>
class B : public myClass<T>
{
public:
B(T b, T t) : myClass<T>(t)
{
this->b = b;
}
void myPrint()
{
cout << "b: " << b << " t: " << t << endl;
}
private:
T b;
};
void main()
{
// 类模板使用
myClass<int> t(10);
// 派生普通类
A a(1, 2);
a.myPrint();
// 派生模板类
B<double> b(10.1, 20.2);
b.myPrint();
}
(3)类模板语法知识体系
template <typename T>
class Complex
{
public:
Complex(T a,T b){
this->a = a;
this->b = b;
}
void printCom(){
cout << a << " + " << b << "i" << endl;
}
Complex operator+(Complex &a2){
Complex tmp(a + a2.a, b + a2.b);
return tmp;
}
friend ostream& operator<<(ostream &out, Complex &a){
out << "a: " << a.a << " b:" << a.b;
return out;
}
friend Complex mySub(Complex &a1, Complex &a2){
Complex tmp(a1.a - a2.a, a1.b - a2.b);
return tmp;
}
private:
T a;
T b;
};
void main()
{
Complex<int> a1(1, 2);
Complex<int> a2(3, 4);
// 重载 + <<
cout << a1 + a2 << endl;
// 滥用友元函数(友元函数一般重载 << >> 操作符)
{
Complex<int> a3 = mySub(a1, a2);
cout << a3 << endl;
}
}
结论:友元函数只用来进行 左移 右移 操作符重载。
友元函数:用友元函数重载 << >>
friend ostream& operator<< <T> (ostream &out, Complex<T> &c3) ;
// 类的前置声明 函数的前置声明
template<typename T>
class Complex;
template<typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);
template <typename T>
class Complex
{
public:
Complex(T a, T b);
void printCom();
Complex operator+(Complex &a2);
friend ostream& operator<< <T> (ostream &out, Complex &a);
// 滥用友元函数
friend Complex<T> mySub <T> (Complex<T> &a1, Complex<T> &a2);
private:
T a;
T b;
};
// 构造函数实现 在类的外部
template <typename T>
Complex<T>::Complex(T a, T b){
this->a = a;
this->b = b;
}
// 成员函数实现 在类的外部
template <typename T>
void Complex<T>::printCom(){
cout << a << " + " << b << "i" << endl;
}
// 重载实现 在类的外部
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &a2){
Complex tmp(a + a2.a, b + a2.b);
return tmp;
}
// 友元函数实现 在类的外部
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &a){
out << "a: " << a.a << " b:" << a.b;
return out;
}
// 不建议使用!
template <typename T>
Complex<T> mySub(Complex<T> &a1, Complex<T> &a2){
Complex<T> tmp(a1.a - a2.a, a1.b - a2.b);
return tmp;
}
void main()
{
Complex<int> a1(1, 2);
Complex<int> a2(3, 4);
// 重载 + <<
cout << a1 + a2 << endl;
// 滥用友元函数(友元函数一般重载 << >> 操作符)
{
// (1)前置申明 (2)类的内部声明 (3)友元函数实现 (4)友元函数调用
Complex<int> a3 = mySub<int>(a1, a2);
cout << a3 << endl;
}
}
要包含.cpp (.hpp
)
complex.h
template <typename T>
class Complex
{
public:
Complex(T a, T b);
void printCom();
Complex operator+(Complex &a2);
friend ostream& operator<< <T> (ostream &out, Complex &a);
private:
T a;
T b;
};
complex.cpp
#include "complex.h"
// 构造函数实现 在类的外部
template <typename T>
Complex<T>::Complex(T a, T b){
this->a = a;
this->b = b;
}
// 成员函数实现 在类的外部
template <typename T>
void Complex<T>::printCom(){
cout << a << " + " << b << "i" << endl;
}
// 重载实现 在类的外部
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &a2){
Complex tmp(a + a2.a, b + a2.b);
return tmp;
}
// 友元函数实现 在类的外部
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &a){
out << "a: " << a.a << " b:" << a.b;
return out;
}
test.cpp
#include "complex.cpp"
void main()
{
Complex<int> a1(1, 2);
Complex<int> a2(3, 4);
// 重载 + <<
cout << a1 + a2 << endl;
}
(4)类模板中的static关键字
从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个 static 数据成员;
和非模板类的 static 数据成员一样,模板类的 static 数据成员也应该在文件范围定义和初始化;
每个模板类有自己的类模板的 static 数据成员副本;
// 类模板中的static关键字
template <typename T>
class myClass
{
public:
static T m_a;
};
// 初始化static
template <typename T>
T myClass<T>::m_a = 0;
// m_a 应该是 每一种类型的类 使用自己的m_a
// --> int AA1::m_a = 0;
// --> char AA2::m_a = 0;
void main()
{
myClass<int> a1, a2, a3;
a1.m_a = 1;
a2.m_a++;
a3.m_a++;
cout << myClass<int>::m_a << endl;
myClass<char> b1, b2, b3;
b1.m_a = ‘a‘;
b2.m_a++;
b3.m_a++;
cout << myClass<char>::m_a << endl;
}
编译器并不是把函数模板处理成能够处理任意类的函数;
编译器从函数模板通过具体类型产生不同的函数;
编译器会对函数模板进行两次编译:
在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
(5)类模板在开发中的应用
--> stl 容器的概念
结论1:如果把Teacher放入到MyVector数组中,并且Teacher类的属性含有指针,就是出现深拷贝和浅拷贝的问题。
结论2:需要Teacher封装的函数有: 1) 重写拷贝构造函数 2) 重载等号操作符 3) 重载左移操作符。
提高:所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以 STL 容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。
MyVector.h
template <typename T>
class MyVector
{
public:
MyVector(int size = 0);
MyVector(const MyVector &obj);
~MyVector();
friend ostream& operator<< <T> (ostream &out, MyVector &myV);
T& operator[](int index);
int getLen()
{
return m_len;
}
private:
T* m_space;
int m_len;
};
MyVector.cpp
#include "MyVector.h"
//MyVector<int> myV1(10);
template <typename T>
MyVector<T>::MyVector(int size = 0)
{
m_space = new T[size];
m_len = size;
}
//MyVector<int> myv2 = myv1;
template <typename T>
MyVector<T>::MyVector(const MyVector &obj)
{
// 根据大小分配内存
m_len = obj.m_len;
m_space = new T[m_len];
// copy data
for (int i=0; i<m_len; i++)
{
m_space[i] = obj.m_space[i];
}
}
template <typename T>
MyVector<T>::~MyVector()
{
if (m_space != NULL)
{
delete[] m_space;
m_space = NULL;
m_len = 0;
}
}
// 重载[]
template <typename T>
T& MyVector<T>::operator[](int index)
{
return m_space[index];
}
// 重载<<
template <typename T>
ostream& operator<<(ostream &out, MyVector<T> &myV)
{
for (int i=0; i<myV.getLen(); i++)
{
out << myV.m_space[i] << " ";
}
return out;
}
MyVector_test.cpp
#include "MyVector.cpp"
class Teachar
{
public:
// 默认构造函数
Teachar() {
m_age = 18;
m_pname = new char[1];
strcpy(m_pname, "");
//strcpy(m_name, "");
}
// 有参构造函数
Teachar(char* name, int age) {
m_age = age;
m_pname = new char[strlen(name) + 1];
strcpy(m_pname, name);
//strcpy(m_name, name);
}
// 拷贝构造函数
Teachar(const Teachar &obj) {
m_age = obj.m_age;
m_pname = new char[strlen(m_pname) + 1];
strcpy(m_pname, obj.m_pname);
}
//析构函数 释放panme指向的内存空间
~Teachar() {
if (m_pname != NULL)
{
delete[] m_pname;
m_pname = NULL;
m_age = 18;
}
}
void prinT() {
cout << m_pname << " , " << m_age << endl;
//cout << m_name << " , " << m_age << endl;
}
// 避免浅拷贝 重载 =
Teachar& operator=(Teachar &obj)
{
// 1.清空内存
delete[] m_pname;
m_pname = NULL;
m_age = 18;
// 2.分配内存
m_pname = new char[strlen(obj.m_pname) + 1];
// 3.copy
strcpy(m_pname, obj.m_pname);
m_age = obj.m_age;
return *this;
}
// 重载 <<
friend ostream& operator<<(ostream &out, Teachar &obj)
{
out << obj.m_pname << " , " << obj.m_age << endl;
return out;
}
private:
//char m_name[32];
// 属性变成 char *panme, 构造函数里面 分配内存
char* m_pname;
int m_age;
};
void main()
{
// 存储普通类型 int
{
// 定义变量
MyVector<int> myV1(10);
// 赋值,重载[]
for (int i = 0; i < myV1.getLen(); i++)
{
myV1[i] = 1 + i;
cout << myV1[i] << " ";
}
cout << endl;
// 拷贝构造函数
MyVector<int> myV2 = myV1;
// 打印,重载<<
cout << myV2 << endl;
}
// 存储普通类型 char
{
// 定义变量
MyVector<char> myVc(26);
// 赋值
for (int i = 0; i < myVc.getLen(); i++)
{
myVc[i] = ‘a‘ + i;
}
cout << myVc << endl;
}
// 存储复杂类型 Teacher
{
Teachar t1("teacher1", 31), t2("teacher2", 32), t3("teacher3", 33), t4("teacher4", 34);
// 存入 MyVector
MyVector<Teachar> tArray(4);
// 避免浅拷贝 重载 = 重写拷贝构造函数
tArray[0] = t1;
tArray[1] = t2;
tArray[2] = t3;
tArray[3] = t4;
for (int i=0; i<4; i++)
{
tArray[i].prinT();
}
// Teacher 增加 <<
cout << tArray;
}
// 存储复杂类型 Teacher*
{
Teachar t1("teacher1", 31), t2("teacher2", 32), t3("teacher3", 33), t4("teacher4", 34);
MyVector<Teachar*> tArray(4);
tArray[0] = &t1;
tArray[1] = &t2;
tArray[2] = &t3;
tArray[3] = &t4;
for (int i = 0; i < 4; i++)
{
Teachar *tmp = tArray[i];
tmp->prinT();
}
}
}
原文:https://www.cnblogs.com/2dx3906/p/13216421.html