1 面向对象编程
1.1 OOP概述
面向对象程序设计的核心思想是数据抽象、继承和动态绑定。
- 通过数据抽象可以将类的接口与实现分离;
- 使用继承可以定义相似的类型并对其相似关系建模;
- 使用动态绑定可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。
1.2 基类与派生类
1.3 虚函数与抽象基类
1.4 拷贝控制与继承
1.5 作用域与继承
2 泛型编程
面向对象编程和泛型编程都能处理在编写程序时不知道类型的情况。不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而泛型编程中,在编译时就能获知类型了。
模板是C++中泛型编程的基础,一个模板就是一个创建类或函数的蓝图或者说公式。
2.1 函数模板
2.1.1 定义、使用、实例化
template <typename T>
- 定义:模板定义以关键字
template
开始,后跟一个模板参数列表,列表中包含一个或多个模板参数。 - 使用:当使用模板时,(可以隐式地或显式地)指定模板实参,将其绑定到模板参数上。
- 实例化:当调用一个函数模板时,编译器通常用函数实参来推断模板实参,用推断出的模板参数实例化一个特定版本的函数。这些编译器生成的版本通常被称为模板的实例。
2.1.2 模板参数
- 模板类型参数:一般来说,可以将类型参数看作类型说明符。
- 非类型模板参数:一个非类型参数表示一个值而非一个类型。通过一个特定的类型名而不是关键字class或typename来指定非类型参数。非类型模板参数的模板实参必须是常量表达式。
2.1.3 inline、constexpr与模板
inline和constexpr说明符放在模板参数列表之后,返回类型之前。
2.1.4 模板编译
只有当实例化出模板的一个特定版本时,编译器才会生成代码。模板的头文件通常既包括声明也包括定义。
2.2 类模板
与函数模板不同的是,编译器不能为类模板推断模板参数类型。为了使用类模板,必须提供额外信息。
2.3 模板实参推断
2.4 重载与模板
2.5 模板特例化
3 IO库
4 容器
5 动态内存
5.1 内存管理概述
- 在C++中,动态内存管理是通过一对运算符来完成的,即
new
、delete
。
- new:在动态内存中为对象分配空间并返回一个指向该对象的指针
- delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存
- 为了更容易、更安全地使用动态内存,新的标准库提供了智能指针来管理动态对象。所在头文件
<memory>
- shared_ptr:允许多个指针指向同一对象
- unique_ptr:”独占“所指向的对象
- weak_ptr:伴随类,是一种弱引用,指向shared_ptr所管理的对象
5.2 shared_ptr
智能指针也是模板。
程序使用动态内存出于以下三种原因之一:
- 程序不知道自己需要使用多少对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
5.2.1 定义与初始化
- 定义:
shared_ptr<T> sp
- 初始化:默认初始化的智能指针中保存着一个空指针,解引用一个智能指针返回它指向的对象
- 支持的操作:…
make_shared
:在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。最安全的分配和使用动态内存的方法。eg:auto p = make_shared<T>()
5.2.2 拷贝、赋值、销毁
- 引用计数:当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象,可以认为每个shared_ptr都有一个关联的计数器,通常称为引用计数。
- 拷贝一个shared_ptr时,计数器都会递增。如用一个shared_ptr初始化另一个shared_ptr,或者将它作为参数传递给一个函数,或者作为函数的返回值
- 给shared_ptr赋予一个新值或者被销毁时,计数器会递减
- 销毁:
当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。它是通过shared_ptr的析构函数来完成销毁工作的。
- 析构函数控制此类型的对象销毁时执行什么操作
- 析构函数一般用来释放对象所分配的资源
当动态对象不再被使用时,shared_ptr类会自动释放动态对象,自动释放相关联的内存。
5.2.3 直接管理内存
自己直接管理内存的类与使用智能指针的类不同,它们不能依赖类对象拷贝、赋值和销毁操作的任何默认定义。
- 动态分配
- new分配:默认情况下,动态分配的对象是默认初始化的
- 动态分配的const对象:一个动态分配的const对象必须进行初始化
- 定位new:
int *p = new (nothrow) int;
- 释放动态内存
- delete释放:销毁给定的指针指向的对象,释放对应的内存
- 生存期:对于一个由内置指针管理的动态对象,直到被显式释放前它都是存在的
- 空悬指针:当delete一个指针后,指针值就变为无效了,但是在很多机器上指针仍然保存着(已经释放了的)动态内存的地址,在delete之后,指针就变成了空悬指针。
5.2.4 shared_ptr与new结合
- 接受指针参数的智能指针构造函数时explicit的,所以,不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针。
shared_ptr<T> p(new T());
- 当临时对象被销毁时,它所指向的内存会被释放。因此,不要混合使用普通指针和智能指针
- 异常
- 智能指针:即使程序块过早结束,智能指针类也能确保在内存不再需要时将其释放
- 内置指针:在new之后,对应的delete之前,如果发生异常,则内存不会释放
- 哑类:自定义删除器来代替delete
5.3 unique_ptr
5.3.1 定义与初始化
- 定义:unique_ptr没有类似的make_shared函数。定义时需要将其绑定到一个由new返回的指针上。
- 初始化:初始化unique_ptr必须采用直接初始化的形式。
5.3.2 拷贝、赋值、转移、销毁
- 拷贝、赋值:由于一个unique_ptr拥有它指向的对象,因此其不支持普通的拷贝或赋值操作。
- 转移:可以通过调用
release
或reset
将指针的所有权从一个(非const)unique_ptr转移给另一个。 - 销毁:重载一个默认的删除器会影响到unique_ptr类型以及如何构造(reset)该类型的对象。在创建或reset这种unique_ptr类型的对象时,必须提供一个指定类型的可调用对象(删除器)。
可调用对象:对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它是可调用的。
- 函数
- 函数指针
- bind创建的对象
- lambda表达式
- 重载了函数调用运算符的类
5.4 weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针。创建时要用一个shared_ptr来初始化它。
5.5 动态数组
5.5.1 new和数组
- 分配并初始化一个对象数组。
5.5.2 allocator类
- 分配和初始化分离。
<memory>