5.3 弱指针

弱指针(weak_ptr 是用来同共享指针shared_ptr) 配合使用的智能指针,它不参与引用计数,目的是避免形成指针环链。

我们先来看一个使用共享指针导致形成指针环链,最终被管理对象无法释放的例子。

// filename: ptr_circle.cpp
#include <iostream>
#include <vector>
#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:
        // 使用共享指针指向下一个点
        shared_ptr<Point> next_point; // 指向图形的下一个点
    public:
        float x;
        float y;
};

vector<shared_ptr<Point>> get_triangle() {
    vector<shared_ptr<Point>> points;
    shared_ptr<Point> ptr1(new Point(0.0, 1.0)); // 引用计数为1
    shared_ptr<Point> ptr2(new Point(1.0, 0.0)); // 引用计数为1
    shared_ptr<Point> ptr3(new Point(1.0, 1.0)); // 引用计数为1
    ptr1->next_point = ptr2;  // 共享指针引用计数变为2
    ptr2->next_point = ptr3;
    ptr3->next_point = ptr1;
    points.push_back(ptr1);
    points.push_back(ptr2);
    points.push_back(ptr3);
    return points;
}

int main(int argc, char * argv[]) {

    std::cout << "-- 进入 main 函数 --" << std::endl;
    {
        std::cout << "- 进入内部作用域 -" << std::endl;

        vector<shared_ptr<Point>>  ptr_shape = get_triangle();

        cout << "- 即将离开内部作用域 -" << endl;
    }
    // 此时所有的 被管理的对象的引用计数为 1,被管理对象不会被释放
    cout << "-- 已离开内部作用域 --" << endl;
    return 0;
}

编译和运行结果如下:

weimz@mzstudio:~/old$ g++ -o ptr_circle ptr_circle.cpp
weimz@mzstudio:~/old$ ./ptr_circle
-- 进入 main 函数 --
- 进入内部作用域 -
Point(0,1)
Point(1,0)
Point(1,1)
- 即将离开内部作用域 -
-- 已离开内部作用域 --

可见上述 Point 类虽然使用了共享指针,但 Point 类创建的三个对象形成了环链。对象没有释放。内存丢失。解决这一问题的方式是对可能形成环链的对象使用弱指针,避免增加引用计数。即上述 Point类的的成员变量 next_point 使用弱指针可以解决这一问题。

语法

// 创建弱指针并直接管理单个对象
std::weak_ptr<> ptr(对象);

// 创建空弱指针
std::weak_ptr<> ptr;

常用成员函数

成员函数
说明
reset
重新设定管理对象指针。
swap
交换两个同类型智能指针管理的对象。
operator =
修改弱指针的指向。
use_count
返回管理对象的共享指针的引用计数(弱指针没有引用计数)。
operator bool
判断是否为空指针。
expired
检查引用计数是否已经是 0,即此弱指针已经无效,无效返回 true
lock
返回一个共享指针(需要用变量绑定),引用计数增加1,确保对象不会被销毁。

示例

修改 Point 的成员变量 next_point 为 弱指针

// filename: weak_ptr.cpp
#include <iostream>
#include <vector>
#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:
        // 使用弱指针指向下一个点
        weak_ptr<Point> next_point; // 指向图形的下一个点
    public:
        float x;
        float y;
};

vector<shared_ptr<Point>> get_triangle() {
    vector<shared_ptr<Point>> points;
    shared_ptr<Point> ptr1(new Point(0.0, 1.0)); // 引用计数为1
    shared_ptr<Point> ptr2(new Point(1.0, 0.0)); // 引用计数为1
    shared_ptr<Point> ptr3(new Point(1.0, 1.0)); // 引用计数为1
    ptr1->next_point = ptr2;  // 弱指针引用计数不变
    ptr2->next_point = ptr3;
    ptr3->next_point = ptr1;
    points.push_back(ptr1);
    points.push_back(ptr2);
    points.push_back(ptr3);
    return points;
}

int main(int argc, char * argv[]) {

    std::cout << "-- 进入 main 函数 --" << std::endl;
    {
        std::cout << "- 进入内部作用域 -" << std::endl;

        vector<shared_ptr<Point>>  ptr_shape = get_triangle();

        cout << "- 即将离开内部作用域 -" << endl;
    }
    // 此时所有的 被管理的对象的引用计数为 1,被管理对象不会被释放
    cout << "-- 已离开内部作用域 --" << endl;
    return 0;
}

编译和运行结果如下:

weimz@mzstudio:~/old$ g++ -o weak_ptr weak_ptr.cpp
weimz@mzstudio:~/old$ ./weak_ptr
-- 进入 main 函数 --
- 进入内部作用域 -
Point(0,1)
Point(1,0)
Point(1,1)
- 即将离开内部作用域 -
~Point(0,1)
~Point(1,0)
~Point(1,1)
-- 已离开内部作用域 --

可见弱指针没有对管理对象的引用计数做加一操作,致使共享指针已经可以将引用计数减为零,达到成功释放被管理对象的目的。