本文将接着上文(02 黑马C++_核心编程-CSDN博客),继续更新本人在学习黑马C++过程所整理的笔记(本文序号将继续承接上文)。
本文的使用方法:可以将本文作为一个工具书来使用,当编写程序或者阅读程序遇到不熟悉的知识点的时候,可以用 CTRL+F 按键 搜索关键词,查看该知识点。如:当忘记结构体定义方法,需不需要在括号后加分号,就可以 CTRL+F 搜索 “结构体”关键字,即可跳转到所需内容处。相较于使用 C++ Primer 感觉更加快速、便捷,希望对大家有所帮助。
5.1 函数模板
模板不可以直接使用
5.1.1 基本语法
- 创建函数模板
template<typename T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
- 函数模板调用
int a = 16, b = 20;
// 法1:自动类型推导
Swap(a, b);
// 法2:显示指定类型
Swap<int>(a, b);
5.1.2 注意事项
- 可以用 typename / class
template<class T> // 可以把 typename 改成 class
// 一般可能函数模板用 typename,类模板用 class
- 模板必须要确定出 T 的数据类型
template<class T>
void func() { };
// 需要指明 T 的类型
func<int>();
5.1.3 普通函数和函数模版的调用规则
5.1.4 模版的局限性
- 模板具体化
class Person
{
public:
string name;
int age;
};
// 定义函数模板
template<class T>
bool Compare(T& a, T& b)
{
if (a == b)
return 1;
else
return 0;
}
// 如果使用 Person 数据类型,将会走这个函数
template<> bool Compare(Person& p1, Person& p2)
{
}
5.2 类模板
5.2.1 基本语法
template<class NameType, class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->age = age;
this->name = name;
}
NameType name;
AgeType age;
};
Person<string, int> p1("孙悟空", 999);
5.2.2 类模板与函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
template<class NameType, class AgeType = int> // 默认参数写法
class Person
{
public:
Person(NameType name, AgeType age)
{
this->age = age;
this->name = name;
}
NameType name;
AgeType age;
};
Person<string> p1("孙悟空", 999); // 可以不写第二个参数 int
5.2.3 类模板中成员函数的调用时机
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
5.2.4 类模板对象做函数参数
- 三种传入方式
// 指定传入类型
void myPrint(Person<string, int>& p)
{
cout << p.name << endl;
}
// 参数模板化
template<class T1, class T2>
void myPrint2(Person<T1, T2> &p)
{
cout << typeid(T1).name() << endl; // 输出 T1 的数据类型名称
cout << typeid(T2).name() << endl; // 输出 T2 的数据类型名称
}
// 整个类模板化
template<class T>
void myPrint3(T& p)
{
cout << p.name << endl;
}
5.2.5 类模板与继承
- 如果子类继承的父类是一个类模板,子类在声明时要指出父类的类型
- 如果想灵活指出父类的类型,子类也需要变为类模板
template<class T>
class Base
{ };
class Son : public Base<int> // 需要指明父类中 T 的类型
{ };
template<class T1, class T2>
class Son2 : public Base<T2> // T2指明父类的参数类型
{
public:
T1 a; // T1指明子类的参数类型
};
5.2.6 类模板成员函数类外实现
- 需要前面加上 template 定义 和 作用域加上<T1,T2>
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
T1 name;
T2 age;
};
// 类外实现
template<class T1, class T2> // 需要加上类模板定义
Person<T1,T2>::Person(T1 name, T2 age) // 注意作用域需要加上<T1,T2>
{
this->age = age;
this->name = name;
}
5.2.7 类模板分文件编写
问题:类模板中成员函数创建时机是在调用阶段,导致文件编译时链接不到
解决:
- 直接包含 .cpp 源文件
- 将声明和实现写到同一个文件中,并改后缀名为 .hpp
5.2.8 案例
- 模板数组的深拷贝 ( 拷贝构造、重载=)
template<class T>
class myArray
{
public:
// 拷贝构造
myArray(const myArray& a)
{
this->capacity = a.capacity;
this->size = a.size;
this->arr = new T[a.capacity]; // 在堆区开辟同容量空间
for (int i = 0; i < a.size; i++) // 将已有的值赋值过去
this->arr[i] = a.arr[i];
}
// 重载 = 号,防止浅拷贝问题
myArray& operator=(const myArray& a)
{
// 需要先判断原来堆区是否有数据,如果有需要先释放
if (this->arr != NULL) {
delete[] arr;
this->arr = NULL;
this->capacity = 0;
this->size = 0;
}
this->capacity = a.capacity;
this->size = a.size;
this->arr = new T[a.capacity]; // 在堆区开辟同容量空间
for (int i = 0; i < a.size; i++) // 将已有的值赋值过去
this->arr[i] = a.arr[i];
}
// 析构函数
~myArray()
{
if (this->arr != NULL) {
delete[] this->arr;
this->arr = NULL;
}
}
private:
T* arr;
int capacity;
int size;
};
// 调用
myArray<int> a(5);
至此,C++泛型编程文章已更新完毕,下篇将更新 C++ STL技术。