5. 智能指针

智能指针(Smart pointers) 是 C++11 的新特性,他使用类模板封装了传统的指针并重载了 ->* 运算符,让其可以向普通指针一样进行操作。但他可以在智能指针被销毁时自动调用析构函数来对所指向的对象进行自动的 delete 操作来释放上创建的对象来实现自动的内存管理。

智能指针与传统指针相比有两个显著的作用:

  1. 不会因忘记 delete 导致的内存(和资源)泄漏。
  2. 不会因异常发生时跳过 delete 导致的内存(和资源)泄漏。

C++11 的智能指针共有三种

  1. 独占指针(unique_ptr)。
  2. 共享指针(shared_ptr)。
    • 使用引用计数的方式来管理指向的对象。
  3. 弱指针(weak_ptr)。
    • 弱指针智能用和 shared_ptr 配合使用,不参与引用计数,避免形成指针环链。

智能指针在 <memory> 头文件定义,使用是需要使用 #include <memory> 进行包含。

5.1 独占指针

独占指针(unique_ptr是独占一个动态分配对象的所有权的智能指针。此指针不能再赋值给其他智能指针,即统一时间,只有一个 unique_ptr 指向这个对象。

独占指针(unique_ptr 不允许拷贝,即独占指针没有拷贝构造函数和拷贝赋值函数,但独占指针支持移动语义(std::move)。移动后,原指针变为空独占指针。

独占指针的运行效率非常高,几乎同普通指针一样快。

语法

// 创建独占指针并直接管理单个对象
std::unique_ptr<> ptr(new (构造函数实参1, 构造函数实参2, ...));

// 创建独占指针并直接管理多个对象的数组
std::unique_ptr<[]> ptr(new [元素个数]{...});

// 创建空独占指针,后面需要使用 reset 成员函数为其赋值
std::unique_ptr<> ptr;

常用成员函数

成员函数
说明
reset
重新设定管理对象指针,旧的管理对象会被释放(delete).
release
释放管理对象,返回管理对象的指针。同时将此独占指针置空。
swap
交换两个同类型智能指针管理的对象。
get
返回管理对象的普通指针。
operator bool
判断是否为空指针。

示例

创建独占指针 ptr1 让其指向 Point 类型的对象,当 ptr1 销毁时会自动释放 ptr1 指向的对象。

创建空独占指针 ptr2 后面再使用 reset 成员函数指向 新的对象。

创建空独占指针 ptr3 管理对象的数组。

// filename: unique_ptr.cpp
#include <iostream>
#include <memory>

using namespace std;

class Point {
    public:
        Point(float ax=0, float ay=0):x(ax), y(ay) {
            cout << "Point(" << x << "," << y << ")\n";
        }
        ~Point() {
            cout << "~Point(" << x << "," << y << ")\n";
        }
        // 定义为虚函数
        void info(void) {
            cout << "点(" << x << "," << y << ")\n";
        }
    public:
        float x;
        float y;
};


int main(int argc, char * argv[]) {
    std::cout << "-- 进入 main 函数 --" << std::endl;
    {
        std::cout << "- 进入内部作用域 -" << std::endl;

        unique_ptr<Point> ptr1(new Point(1.1, 1.2));
        unique_ptr<Point> ptr2;
        // ptr2 = ptr1; // 报错,不允许
        ptr2.reset(new Point(2.1, 2.2));
        unique_ptr<Point[]> ptr3(new Point[2]{Point(3.1, 3.2), Point(4.1, 4.2)});

        // 使用 -> 访问成员
        ptr1->info();
        ptr2->info();
        // 使用 * 解引用
        (*ptr1).info();
        (*ptr2).info();

        cout << "- 即将离开内部作用域 -" << endl;
    } // 离开作用域,ptr1 自动调用析构函数,内存被释放
    cout << "-- 已离开内部作用域 --" << endl;
    return 0;
}

编译和运行结果如下

weimz@mzstudio:~/old$ g++ -o unique_ptr unique_ptr.cpp
weimz@mzstudio:~/old$ ./unique_ptr
-- 进入 main 函数 --
- 进入内部作用域 -
Point(1.1,1.2)
Point(2.1,2.2)
Point(3.1,3.2)
Point(4.1,4.2)(1.1,1.2)(2.1,2.2)(1.1,1.2)(2.1,2.2)
- 即将离开内部作用域 -
~Point(4.1,4.2)
~Point(3.1,3.2)
~Point(2.1,2.2)
~Point(1.1,1.2)
-- 已离开内部作用域 --

注意

独占指针 一旦绑定管理对象,则不要使用原指针释放对象,否则可能导致不可预期的结果。如:

Point * p = new Point(1.2, 3.4);
unique_ptr<Point> ptr1(p);
delete p;  // 释放对象
// 之后销毁 ptr1 时就会操作野指针造成重复释放。