Appearance
C++高级知识点
模板
引子: 数学里,就加法而言,有整数加法,有小数加法。如果用程序表达,就需要定义好类型,再去进行操作。但实际上的步骤是:
- 有 加法操作 这种概念
- 具体到某个问题,才考虑是整数加法还是小数加法
所以,为了能够将 数据类型的定义 延后到调用的时候,可以先给定一个可以替代任何类型的标志。这种"先利用标志替代数据类型"的思想,就是泛型思想,利用这种思想的编程,就是泛型编程。在C++里,这种技术叫做 模板
cpp
// 格式
// template<typename T>
// 函数声明或定义或类
/*
template 声明创建模板
typename 表明后面的符号是一种数据类型,可用class代替
T 数据通用类型,通常为大写字母,可用其他字母替换
类模板没有自动类型推导
类模板在模板参数列表中可以有默认参数
*/
template<typename T>
void addNum(T a, T b){
return a + b;
}
int main()
{
//1、自动类型推导
//addNum(1, 2);
//2、显示指定类型
addNum<float>(1.0f, 2.0f);
// 省略system和return
}
注意:
1、必须推导出一致的数据类型T才可以使用
addNum(1, 2.0f);
这种就无法推导出一致的数据类型,所以运行时会报错
2、必须确定出T的数据类型才可以使用模板
普通函数与函数模板的区别
- 普通函数调用时可以发生自动类型推导(隐式类型转换)
- 函数模板调用时,在自动类型推导则不会发生隐式类型转换
- 利用显示指定类型的方式,可以发生隐式类型转换
调用规则
- 如果,函数模板和普通函数都可以实现,优先普通函数
- 如果,函数模板可以产生更好的匹配,优先函数模板
- 可以通过 空模板参数列表 来强制调用函数模板
- 函数模板也可以发生重载
- 类模板中成员函数在调用时才去创建,因为在运行前,无法确定类型
类模板对象做函数参数
- 指定传入的类型(常用)
- 参数模板化
- 整个类模板化
cpp
template<class T1,class T2>
class Person {
public:
Person(T1 name, T2 age) {
this->m_name = name;
this->m_age = age;
}
void showPerson() {
cout << this->m_name << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};
void printPerson1(Person<string, int>& p) {
p.showPerson();
}
void test01(){
Person<string, int> p("张三", 25);
printPerson1(p);
}
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p) {
p.showPerson();
cout << typeid(T1).name(); // 查看类型
}
void test02() {
Person<string, int> p("李四", 33);
printPerson2(p);
}
template<class T>
void printPerson3(T& p) {
p.showPerson();
}
void test03() {
Person<string, int> p("王五", 3);
printPerson3(p);
}
类外实现模板类的函数
cpp
template<typename T>
class Person {
public:
void setA(T);
T m_a;
};
template<class T>
void Person<T>::setA(T a) {
m_a = a;
cout << m_a << endl;
}
类模板中,成员函数的创建时机是在调用阶段,导致分文件编写时链接不到。可以:
- 直接包含.cpp源文件 解决
- 将声明和实现写到同一个文件中,并更改后缀名为.hpp(头文件重命名,.h后面加pp。这是个约定的名称,非强制)
类模板与友元
- 全局函数类内实现:直接在类内声明友元即可
- 全局函数类外实现:需要提前让编译器知道全局函数的存在
cpp
template<class T1, class T2>
class Person;
template<class T1, class T2>
void printPerson2(Person<T1, T2> p) {
cout << p.m_name;
}
// 以上代码块用于声明类和实现全局函数的类外实现
template<class T1, class T2>
class Person {
friend void printPerson1(Person<T1, T2> p) {
cout << p.m_name << p.m_age << endl;
}
friend void printPerson2<>(Person<T1, T2> p); // 注意这里函数名后的尖括号,它用于保证正常识别类型
public:
Person(T1 name, T2 age) {
this->m_name = name;
this->m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
void test01() {
Person<string, int>p("Tom", 20);
printPerson2(p);
}
STL(Standard Template Library, 标准模板库)
STL, 广义上分为:容器(Container) 算法(Algorithm) 迭代器(Iterator)
容器和算法之间,通过迭代器进行无缝连接
STL几乎所有代码都采用了模板类或者模板函数
组件 | 说明 |
---|---|
容器 | 各种数据结构,如vector、list、deque、set、map等, 用于存放数据 |
算法 | 各种常用算法,如sort、find、copy、for_each等 |
迭代器 | 扮演了容器与算法之间的胶合剂 |
仿函数 | 行为类似函数,可作为算法的某种策略 |
适配器 | 用来修饰容器或者仿函数或者迭代器接口的东西 |
空间配置器 | 负责空间的配置与管理 |
·容器
可容纳数组、链表、树、栈、队列、集合、映射表等。有两类:
序列式容器:强调值的顺序,元素位置固定
关联式容器:二叉树结构,元素在物理上没有严格的顺序关系
·算法
计算的方法
·迭代器
先简单理解为指针
Vector容器
容器:vector
算法:for_each
迭代器:vector<int>::iterator
cpp
// 加上两个头文件
#include <vector>
#include <algorithm>
void myPrint(int val) {
cout << val << endl;
}
void test01() {
vector<int> v;
// 插入数据(尾插)
v.push_back(10);
// 迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin();
vector<int>::iterator itEnd = v.end(); // v.end()指向最后一个元素的后一位地址
// 第一种遍历方式
/*while (itBegin != itEnd) {
cout << *itBegin << endl;
itBegin++;
}*/
// 第二种遍历方式
/*for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}*/
// 第三种遍历方式 利用STL提供的遍历方法 (推荐!!)
for_each(v.begin(), v.end(), myPrint);
}