目录:点我
一、智能指针模板类
智能指针是行为类似于指针的类对象,但这种对象还有其他功能,下面介绍三个可帮助管理动态内存分配的智能指针模板:
对于上面的函数,每当调用时,该函数都分配堆中的内存,但从不收回,导致内存泄漏,只需在
return 前添加 delete ps 即可释放内存。但是通常会忘记这一步,而且如果存在其他异常情况导致没有执行它,也会导致内存泄漏:遇到这种情况,就期望指针 ps 是一个对象,这样当它过期时就可以使用析构函数删除指向的内存,为此 C++ 提供了智能指针模板,其中
auto_ptr 是 C++ 98 提供的方案,C++ 11 已将其摒弃,并提供了另外两种:unique_ptr, shared_ptr ,它们都定义了类似指针的对象,可以将 new 获得的地址赋给它,并且当智能指针过期时,这些内存将自动被释放。要创建智能指针对象,必须包含头文件 memory ,然后使用通常的模板语法来实例化所需类型的指针:
从写 remodel 方法:
注意,智能指针模板位于名称空间 std 中,下面是一个使用例子:
所有智能指针都有一个 explicit 构造函数,它将指针作为参数,因此不需要自动将指针转换为智能指针对象。
需要避免下面这种情况:
当 p 过期时,程序将把 delete 运算符用于非堆栈内存,这是错误的。
相同智能指针之间赋值也需要注意:
这样做会导致两个指针指向同一个内存,然后当它们相继过期时会对其进行两次 delete ,解决方法有多种:
- 定义赋值运算符:使之成为深复制,即再创建一个副本,使两个指针指向不同的副本;
- 建立所有权概念:对于特定的对象,只有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权。unique_ptr 和 auto_ptr 采用此策略,但前者更严格;
- 创建智能更高的指针,根据引用特定对象的智能指针数,这称为引用计数(reference counting)。shared_ptr 采用此策略。
下面举一个不适用于 auto_ptr 的例子:
由于 p1 丧失所有权,因此变成一个空指针,此时访问 p1 发生错误;如果采用 shared_ptr 则能正常工作,因为二者都指向同一块区域;如果采用 unique_ptr 则会在编译过程中察觉错误,因此这样更安全。
再来看下一种情况:
此时由于 demo 函数返回一个临时的 unique_ptr ,然后 ps 接管了原本返回的 unique_ptr 所有的对象,而返回的 unique_ptr 被销毁,这没有问题,因为 ps 拥有了 string 对象的所有权。这样做的另一个好处是 demo 函数返回的临时 unique_ptr 很快被销毁,没有机会使用它访问无效数据,因此编译器允许这种赋值。
编译器是如何区分两种情况的呢?答案是判断右值:
也就是说,如果源 unique_ptr 是一个临时右值,编译器允许赋值,反之不行。因此 unique_ptr 优于 auto_ptr ,因为后者将允许这两种赋值。如果一定要使用第一种方式赋值,可以采用
std::move() ,该函数类似于 demo ,将返回一个 unique_ptr 对象:unique_ptr 优于 auto_ptr 的另一个原因是 unique_ptr 可以用于数组,而 auto_ptr 不能。因此 unique_ptr 可与 new [] 配套使用,而 auto_ptr 只能与 new 配套使用。
根据不同智能指针的特点,若有多个智能指针同时指向同一个对象,则使用 shared_ptr ,否则建议使用 unique_ptr 。
二、泛型编程
STL 是一种泛型编程,面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用代码,但理念决然不同。泛型编程旨在编写独立于数据类型的代码。
1. 迭代器
顾名思义,迭代器就是用于迭代获取 STL 中不同容器的方式:
从细节上看,二者的实现存在差异;但从功能上看,二者是相同的。因此泛型编程注重的就是算法的功能,而不关注具体实现,对于相同的功能,要求给出一个相同的函数接口和相同的使用方法,因此迭代器需要具备以下特征:
- 应能够对迭代器执行解除引用的操作,以便能够访问它引用的值。
- 应能够将一个迭代器赋给另一个;
- 应能够将一个迭代器与另一个进行比较,判断是否相等;
- 应能够使用迭代器遍历容器中的所有元素;
这样对于查找函数来说,可以如下编写:
还可以使用两个指针标记容器的起始与结束:
此处只给出了指针的迭代器版本,对于不同的容器,所使用的迭代器不一定是指针,也可能是对象,但必须要满足泛型编程的准则。
2. 迭代器类型
不同算法对迭代器要求不同,例如查找算法需要定义 ++ 运算符,以便能够遍历整个容器,但它要求只能读取数据而不能修改。而排序算法要求能随机访问,以便能交换两个不相邻的元素。STL 定义了 5 种迭代器:
- 输入迭代器:针对程序的角度,从容其中获取数据称为输入,因此输入迭代器可被程序用来读取容器中的信息,支持 ++ 运算符。但是对于两次迭代器遍历,它们的顺序可能会发生改变;输入迭代器被递增后,也不能保证其先前的值仍然可以被解除引用。该算法应当是单同行的,不依赖于前一次遍历时的迭代器值,也不依赖于本次遍历中前面的迭代器值。
- 输出迭代器:用于将信息从程序传输给容器的迭代器,允许引用让程序能够修改容器值,而不能读取。也就是说,对于单同行只读算法,可以使用输入迭代器;对于单同行只写算法,可以使用输出迭代器。
- 正向迭代器:与前两种相似,只能使用 ++ 运算符来遍历容器,但是它总是按照相同的顺序遍历值。另外,将正向迭代器递增后,仍然可以对前面的迭代器值解除引用,并可以得到相同的值。正向迭代器既可以读取数据,也可以修改数据:
- 双向迭代器:支持递增、递减两种运算符。
- 随机访问迭代器:
表达式 | 描述 |
a + n | 指向 a 所指向的元素后的第 n 个元素 |
n + a | 与 a + n 相同 |
a - n | 指向 a 所指向的元素前的第 n 个元素 |
r += n | 等价于 r = r + n |
r -= n | 等价于 r = r - n |
a[n] | 等价于 *(a + n) |
b - a | 结果为这样的 n 值,即 b = a + n |
a < b | 如果 b - a > 0 ,则为真 |
a > b | 如果 b < a ,则为真 |
a >= b | 如果 !(a < b) ,则为真 |
a <= b | 如果 !(a > b) ,则为真 |
三、函数对象
很多 STL 算法都使用函数对象——也叫函数符(functor),它是可以以函数方式与 () 结合使用的任意对象。这包括函数名、指向函数的指针和重载了 () 运算符的类对象:
这样重载 () 运算符将使得能够像函数那样使用 Linear 对象。对于下面这个函数存在一个问题,如何定义第三个参数?
首先考虑使用函数指针,但是函数指针需要制定参数类型,因此不可用。然后考虑模板:
此时,便完成了对第三个参数的定义。由上面的例子便可得到函数符的概念:
- 生成器(generator)是不用参数就可以调用的函数符;
- 一元函数(unary function)是用一个参数可以调用的函数符;
- 二元函数(binary function)是用两个参数可以调用的函数符;
- 返回 bool 值的一元函数是谓词(predicate);
- 返回 bool 值的二元函数是二元谓词(binary predicate)。
原创存证 · 本地哈希 · 4400fc8de8f6...
算法:SHA-256
时间:2026-10-15
哈希:
4400fc8de8f6728fd85faed2ac44c6046af2a6b35fe2ffc5e3cf56a911dd012d该信息用于辅助证明当前内容版本,不等同于法律公证或版权登记。
- 作者:BeZer0
- 链接:https://blog.bezer0.cn/%E8%80%83%E7%A0%94/3fa0bc13-b7ac-8086-a2fe-f7d04e089525
- 声明:本文为原创文章,采用 CC BY-NC-SA 4.0 协议,转载请注明作者和原文链接。

