文章目录
第一章:
1.模板
1.1 模板概念
模板就是建立通用的模具,提高复用性。
特点:
-
模板不可直接使用,它只是一个框架
-
模板的通用并不是万能的
1.2 函数模板
作用:建立一个通用的函数,让其函数返回值类型和形参类型可不具体设定,用一个虚拟的类型来表示。
C++另一种编程思想称为泛型编程,运用的主要技术就是模板。
C++中提供了两种模板机制:函数模板和类模板。
1.2.1 函数模板语法
语法:
template<typename T>
函数声明或定义
-
template:声明创建模板
-
typename:表其后面的符号是一种数据类型,可用class代替
-
T:通用的数据类型,名称可替换,通常为大写字母
两种方式使用函数模板:
-
自动类型推导
-
显示指定类型
#include <iostream>
using namespace std;
template<typename T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
//Swap(a, b); //自动类型推导
Swap<int>(a, b); //显示指定类型
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
1.2.2 函数模板注意事项
-
自动类型推导,需推导出一致的数据类型T,才可使用
-
模板需确定T的数据类型,才可使用
#include <iostream>
using namespace std;
template<typename T>
void Swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'A';
Swap(a, b); //正确
//Swap(a, c); //报错,自动类型推导,需推导出一致的数据类型T,才可使用
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
template <typename T>
void func()
{
cout << "func 函数的调用" << endl;
}
void test02()
{
func<int>(); //正确
//func(); //报错,模板需确定T的数据类型,才可使用
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
1.2.3 函数模板案例
-
使用函数模板封装一个排序的函数,可对不同数据类型数组进行排序
-
排序规则从大到小,排序算法为选择排序
-
分别利用char数据和int数组进行测试
#include <iostream> using namespace std; template<class T> void MySwap(T& a, T& b) //交换函数 { T temp = a; a = b; b = temp; } template <class T> void MySort(T arr[], int len) //排序函数 { for (int i = 0; i < len; i++) { int max = i; //默认最大值的下标 for (int j = i + 1; j < len; j++) { if (arr[max] < arr[j]) { max = j; } } if (max != i) { MySwap(arr[max], arr[i]); } } } template <class T> void printArray(T arr[], int len) //打印数组函数 { for (int i = 0; i < len; i++) { cout << arr[i] << " "; } cout << endl; } void test01() { //测试char类型数组 char chArray[10] = "dbcaefg"; int chLength = sizeof(chArray) / sizeof(char); //计算数组长度 MySort(chArray, chLength); printArray(chArray, chLength); //测试int类型数组 int intArray[] = { 6,2,4,3,9,8,1,5,7 }; int intLength = sizeof(intArray) / sizeof(int); 计算数组长度 MySort(intArray, intLength); printArray(intArray, intLength); } int main() { test01(); system("pause"); return 0; }
1.2.4 普通函数与函数模板的区别
-
普通函数调用时可发生自动类型转换(隐式类型转换)
-
函模板调用时,如果用自动类型转换,将不会发生隐式类型转换
-
如果用显示指定类型的方式,可发生隐式类型转换
#include <iostream>
using namespace std;
int myAdd_1(int a, int b)
{
return a + b;
}
template <class T>
int myAdd_2(T a, T b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'a';
cout << myAdd_1(a, c) << endl; //107,普通函数调用时可发生自动类型转换(隐式类型转换)
//cout << myAdd_2(a, c) << endl; //报错,函模板调用时,如果用自动类型转换,将不会发生隐式类型转换
cout << myAdd_2<int>(a, c) << endl; //107,如果用显示指定类型的方式,可发生隐式类型转换
}
int main()
{
test01();
system("pause");
return 0;
}
1.2.5 普通函数与函数模板的调用规则
-
函数模板和普通函数都可实现的情况下,优先调用普通函数
-
函数模板可以产生更好的匹配的情况下,优先调用函数模板
-
可通过空模板参数列表来强制调用函数模板
-
函数模板也可发生重载
#include <iostream> using namespace std; int myAdd(int a, int b) { cout << "普通函数调用" << endl; return a + b; } template <class T> int myAdd(T a, T b) { cout << "函数模板调用" << endl; return a + b; } template <class T> int myAdd(T a, T b, T c) { cout << "函数模板重载调用" << endl; return a + b + c; } void test01() { int a = 10; int b = 20; char ch1 = 'a'; char ch2 = 'b'; myAdd(a, b); //普通函数调用 myAdd<>(a, b); //函数模板调用 myAdd(a, b, 30); //函数模板重载调用 myAdd(ch1, ch2); //函数模板调用 } int main() { test01(); system("pause"); return 0; }
1.2.6 模板的局限性
- 模板的通用性不是万能的
template<class T>
void assignment(T a, T b)
{
a = b
}
如果传入的a和b是数组,就没法实现。
template<class T>
void compare(T a, T b)
{
if(a > b)
{
...
}
}
如果传入的是自定义数据类型,也无法实现。
为了解决这种问题,提供了模板的重载,为特定的类型提供具体化的模板
#include <iostream>
using namespace std;
class Student
{
public:
Student(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
template <class T>
bool myCompare(T &a, T &b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
template<> bool myCompare(Student & p1, Student& p2) //使用具体化Student版本实现代码,具体化优先调用
{
if (p1.m_age == p2.m_age && p1.m_name == p2.m_name)
{
return true;
}
else
{
return false;
}
}
void test01()
{
Student s1("小明", 20);
Student s2("小红", 18);
bool result = myCompare(s1, s2);
if (result)
{
cout << "s1 = s2" << endl;
}
else
{
cout << "s1 != s2" << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
1.3 类模板
作用:建立一个通用类,类中成员数据类型可不具体设定,用一个虚拟的类型表示。
1.3.1 类模板语法
语法:
template<class T>
类
-
template:声明创建模板
-
typename:表其后面的符号是一种数据类型,可用typename代替
-
T:通用的数据类型,名称可替换,通常为大写字母
#include <iostream>
using namespace std;
#include <string>
template <class T1, class T2>
class Student
{
public:
Student(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
void showStuInfo()
{
cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};
void test01()
{
Student<string, int> s1("小明", 18);
s1.showStuInfo();
}
int main()
{
test01();
system("pause");
return 0;
}
1.3.2 类模板与函数模板的区别
-
类模板没有自动类型推导的使用方式
-
类模板在模板参数列表中可以有默认参数
#include <iostream>
using namespace std;
template<class T1, class T2 = int>
class Student
{
public:
Student(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
void showStuInfo()
{
cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};
void test01()
{
//Student s1("小明", 18); //报错,类模板没有自动类型推导
Student<string> s1("小明", 18);
s1.showStuInfo();
}
int main()
{
test01();
system("pause");
return 0;
}
1.3.3 类模板中成员函数创建时机
-
普通类中的成员函数一开始就可创建
-
类模板中的成员函数在调用时才创建
#include <iostream>
using namespace std;
class Student1
{
public:
void printStudent1()
{
cout << "Student1" << endl;
}
};
class Student2
{
public:
void printStudent2()
{
cout << "Student2" << endl;
}
};
template<class T>
class Class
{
public:
T student;
void func1()
{
student.printStudent1();
}
void func2()
{
student.printStudent2();
}
};
void test01()
{
Class<Student1>s1;
s1.func1();
//s1.func2(); //报错,函数调用才会去创建成员函数
}
int main()
{
test01();
system("pause");
return 0;
}
1.3.4 类模板对象做函数参数
三种传入方式:
-
指定传入的类型 — 直接显示对象的数据类型
-
参数模板化 — 将该对象中的参数变为模板进行传递
-
整个类模板化 — 将该对象类型模板化进行传递
#include <iostream>
using namespace std;
template<class T1, class T2>
class Student
{
public:
Student(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
void showStuInfo()
{
cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};
void printStuInfo(Student<string, int>&s) //传入指定类型
{
s.showStuInfo();
}
void test01()
{
Student<string, int>s("小明", 18);
printStuInfo(s);
}
template<class T1, class T2>
void printStuInfo2(Student<T1, T2>& s) //参数模板化
{
s.showStuInfo();
cout << "T1的类型:" << typeid(T1).name() << endl; //T1的类型:class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
cout << "T2的类型:" << typeid(T2).name() << endl; //T2的类型:int
}
void test02()
{
Student<string, int>s("小红", 19);
printStuInfo2(s);
}
template<class T>
void printStuInfo3(T& s) //整个类模板化
{
s.showStuInfo();
cout << "T的类型:" << typeid(T).name() << endl; //T的类型:class Student<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>
}
void test03()
{
Student<string, int>s("小王", 20);
printStuInfo3(s);
}
int main()
{
test01();
test02();
test03();
system("pause");
return 0;
}
1.3.5 类模板与继承
-
当子类继承的父类是一个类模板时,子类在声明的时候,要指定父类中T的类型
-
不知道类型的话,编译器无法给子类分配内存空间
-
想灵活指定父类中T的类型,子类需也变成类模板
#include <iostream>
using namespace std;
template<class T>
class Base
{
public:
T m_age;
};
//class Son :public Base //报错,子类在声明的时候,要指定父类中T的类型
class Son :public Base<int>
{
};
void test01()
{
Son s1;
}
template<class T1, class T2>
class Son2 :public Base<T2> //想灵活指定父类中T的类型,子类需也变成类模板
{
public:
void showType()
{
cout << "T1的类型:" << typeid(T1).name() << endl; //T1的类型:int
cout << "T2的类型:" << typeid(T2).name() << endl; //T2的类型:char
}
T1 m_a;
};
void test02()
{
Son2<int, char> s2;
s2.showType();
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
1.3.6 类模板成员函数类外实现
#include <iostream>
using namespace std;
template<class T1, class T2>
class Student
{
public:
Student(T1 name, T2 age);
void printStuInfo();
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Student<string, int> s("小明", 18);
s.printStuInfo();
}
int main()
{
test01();
system("pause");
return 0;
}
1.3.7 类模板分文件编写
类模板中成员函数创建时机在调用阶段,导致分文件编写时链接不到
解决方法:
-
直接包含.cpp源文件
-
将声明和实现写在同一文件中,更改后缀名.hpp,hpp约定名称,不强制
test.cpp:
#include <iostream>
using namespace std;
//#include "Student.cpp" //解决方法1 - 包含源文件
#include "Student.hpp" //解决方法2 - .h和.cpp内容写一起,改后缀.hpp文件
void test01()
{
Student<string, int>s("小明", 18);
s.printStuInfo();
}
int main()
{
test01();
system("pause");
return 0;
}
Student.cpp:
#include "Student.h"
template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
Student.h:
#pragma once
#include <iostream>
using namespace std;
template<class T1, class T2>
class Student
{
public:
Student(T1 name, T2 age);
void printStuInfo();
T1 m_Name;
T2 m_Age;
};
Student.hpp:
#pragma once
#include <iostream>
using namespace std;
template<class T1, class T2>
class Student
{
public:
Student(T1 name, T2 age);
void printStuInfo();
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
1.3.8 类模板与友元
-
全局函数类内实现 - 在类内声明友元即可
-
全局函数类外实现 - 需提前让编译器知道全局函数的存在
#include <iostream>
using namespace std;
//提前让编译器知道Studet类存在
template<class T1, class T2>
class Student;
//类外实现
template<class T1, class T2>
void printStuInfo(Student<T1, T2>s)
{
cout << "姓名:" << s.m_Name << " 年龄:" << s.m_Age << endl;
}
template<class T1,class T2>
class Student
{
public:
Student(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
//全局函数 - 类内实现
friend void printStuInfo(Student<T1, T2>s)
{
cout << "姓名:" << s.m_Name << " 年龄:" << s.m_Age << endl;
}
//全局函数 - 类外实现
//提前让编译器知道函数的存在
//加空模板参数列表
friend void printStuInfo<>(Student<T1, T2>s);
T1 m_Name;
T2 m_Age;
};
void test01()
{
Student<string, int>s("小明", 18);
printStuInfo(s);
Student<string, int>s2("小红", 19);
printStuInfo(s2);
}
int main()
{
test01();
system("pause");
return 0;
}