C++ 智能指针

 

C++智能指针是一种用于自动管理动态分配内存的对象, 以避免内存泄漏和悬挂指针(dangling pointer)

std::unique_ptr

定义

独占所有权智能指针, 同时只能有一个std::unique_ptr指向对象, 无法被复制, 可通过std::move转移所有权

使用 RAII(Resource Acquisition Is Initialization)原理管理动态内存, 超出作用域时自动调用 delete 释放

操作

创建和初始化

  • 使用new操作符
std::unique_ptr<int> up(new int(10));
  • 使用std::make_unique 函数(C++14引入)
auto up = std::make_unique<int>(20);

访问

使用解引用操作符 *获取对象

auto up = std::make_unique<int>(20);
std::cout << *up << std::endl;

转移所有权

std::unique_ptr 不允许复制, 但可以通过 std::move 转移所有权

auto up = std::make_unique<int>(20);

// 转移所有权, up 变为空
auto up2 = std::move(up);

释放所有权

使用 release 方法释放所有权, 并返回原始指针, 需要手动删除对象以避免内存泄漏

auto up = std::make_unique<int>(20);

// 释放所有权, 返回原始指针
int* p = up.release();
// 手动删除对象
delete p;

重置和重新分配

使用 reset 方法重置 std::unique_ptr, 释放当前对象并管理新对象

如果调用 reset 时不传递任何参数, 则会释放当前对象并使 std::unique_ptr 变为空

auto up = std::make_unique<int>(20);

// 重置并管理新的对象
up.reset(new int(30));

// 释放当前对象, up变为空
up.reset();

自定义删除器

std::unique_ptr 允许自定义删除器, 用于在对象销毁时执行特定清理操作

struct CustomDeleter {
    void operator()(int* p) const {
        std::cout << "deleting int pointer" << std::endl;
        delete p;
    }
};

// 使用自定义删除器
std::unique_ptr<int, CustomDeleter> up(new int(40), CustomDeleter());
#include <iostream>
#include <memory>

int main() {
    // 使用 make_unique 初始化
    std::unique_ptr<int> p1 = std::make_unique<int>(10);
    std::cout << "value: " << *p1 << std::endl;

    // 直接构造
    std::unique_ptr<int> p2(new int(20));

    // 转移所有权
    std::unique_ptr<int> p3 = std::move(p1); // p1 现在为空
    if (!p1) {
        std::cout << "p1 is null after move." << std::endl;
    }

    // 自动释放
    return 0;
} // p2 和 p3 超出作用域时内存会被释放

std::shared_ptr

定义

共享所有权智能指针, 多个std::shared_ptr可指向同一个对象

使用引用计数来管理对象生命周期, 当最后一个std::shared_ptr销毁时引用计数归零, 对象自动销毁释放内存

操作

创建和初始化

使用 std::make_shared 工厂函数来创建和初始化

// 指向一块 10 个 int 类型数据堆内存空间
std::shared_ptr<int> p = std::make_shared<int>(10);

访问

#include <iostream>
#include <memory>

int main() {
    int var = 0xFF;
    printf("var address: %p, var value: %d\n", &var, var);

    std::shared_ptr<int> sp = std::make_shared<int>(var);
    printf("sp value: %p\n", *p);
    return 0;
}

复制和共享

std::shared_ptr 支持复制操作, 可通过赋值将多个 std::shared_ptr 实例共享同一个对象

此时对象的引用计数会增加

std::shared_ptr<int> sp = std::make_shared<int>(10);

// 复制, 引用计数增加
std::shared_ptr<int> sp2 = sp;

引用计数

使用 use_count 方法获取当前 std::shared_ptr引用计数

#include <iostream>
#include <memory>

int main() {
    int value = 0xFF;

    std::shared_ptr<int> sp = std::make_shared<int>(value);
    printf("sp use count: %d\n", sp.use_count());

    std::shared_ptr<int> tp = sp;
    printf("sp use count: %d\n", sp.use_count());
    return 0;
}

获取原始指针

使用 get 方法获取 std::shared_ptr 所管理对象原始指针

不会增加引用计数, 因此在使用原始指针时需要小心, 避免手动删除对象或导致悬空指针

#include <iostream>
#include <memory>

int main() {
    int var = 0xFF;
   
    int *p = &var;
    printf("p value: %p, *p = %d\n", p, *p);

    std::shared_ptr<int> sp = std::make_shared<int>(0xFF);
    printf("*sp value: %d\n", *sp);

    int* gp = sp.get();
    printf("gp value: %p, *gp = %d\n", gp, *gp);
    return 0;
}

std::weak_ptr

弱引用智能指针, 不控制对象生命周期, 用于打破shared_ptr循环引用, 避免无法释放内存

不能直接访问对象, 需要通过std::shared_ptr操作

操作

创建

std::weak_ptr必须和 std::shared_ptr 配合使用

可以从一个 std::shared_ptr 或另一个 std::weak_ptr 对象构造

std::shared_ptr<int> sp = std::make_shared<int>(10);

// 通过 std::shared_ptr 初始化
std::weak_ptr<int> wp = sp;

访问状态

expired 方法用于检查所观察std::shared_ptr 是否已经失效(即所管理对象是否已经被销毁)

std::shared_ptr<int> sp = std::make_shared<int>(10);

std::weak_ptr<int> wp = sp;
if (!wp.expired()) {
    // wp 所观察 std::shared_ptr 仍然有效
}

转换为 std::shared_ptr

lock 方法可尝试转换成std::shared_ptr

如果转换成功(即所观察对象仍然存在), 则返回有效std::shared_ptr, 否则返回空

std::shared_ptr<int> sp = std::make_shared<int>(10);

std::weak_ptr<int> wp = sp;
std::shared_ptr<int> sp2 = wp.lock();
if (sp2) {
    // 转换成功, 可以使用 sp2 访问对象
    printf("*sp value: %d\n", *sp);
} else {
    // 转换失败, 对象已经被销毁
    printf("resource has been released");
}

重置

std::weak_ptr可以通过赋值操作进行重置, 使其不再观察任何 std::shared_ptr

std::weak_ptr<int> wp2;
// wp 现在不再观察任何 std::shared_ptr
wp = wp2;