c++ 智能指针使用注意事项及解决方案

c++11智能指针

shared_ptr

介绍

shared_ptr 是一种智能指针,用于自动管理动态分配的对象的生命周期。它通过引用计数机制来确保当最后一个 shared_ptr 指向一个对象时,该对象会被自动销毁。

注意事项

当两个或多个 shared_ptr 实例相互引用时,会形成一个环形引用,导致引用计数永远不会达到零,从而引起内存泄漏。

示例

复制代码
#include <memory>
#include <iostream>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() {
        std::cout << "A destructor called" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a_ptr;
    ~B() {
        std::cout << "B destructor called" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
    return 0;
}

在这个例子中,A 的实例持有 B 的一个 shared_ptr,而 B 的实例也持有 A 的一个 shared_ptr。这导致它们的引用计数永远不会降到零,因此它们的析构函数永远不会被调用,从而造成内存泄漏。

解决方案

使用 std::weak_ptr

std::weak_ptr 是一种智能指针,它不会增加对象的引用计数。它设计用来解决 shared_ptr 的环形引用问题。你可以将其中一个对象的 shared_ptr 替换为 weak_ptr 来避免环形引用:

复制代码
#include <memory>
#include <iostream>

class B; // 前向声明

class A {
public:
    std::weak_ptr<B> b_ptr; // 使用 weak_ptr 替代 shared_ptr
    ~A() {
        std::cout << "A destructor called" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a_ptr;
    ~B() {
        std::cout << "B destructor called" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
    return 0;
}

在这个修改后的例子中,A 类使用 std::weak_ptr来引用 B 类的实例。这样,A 对 B 的引用不会增加 B 的引用计数。因此,当 main 函数结束时,a 和 b 的引用计数都会降到零,它们所指向的对象将被正确销毁,从而避免了内存泄漏。

weak_ptr

weak_ptr 是一种智能指针,用于观察 std::shared_ptr 所管理的对象,但不拥有该对象。std::weak_ptr 不会增加对象的引用计数,因此不会影响对象的生命周期。它主要用于解决 std::shared_ptr 的环形引用问题。

特点

  • 不拥有对象,只是观察者。
  • 用于监视 std::shared_ptr,但不影响其引用计数。
  • 可以从 std::weak_ptr 创建 std::shared_ptr 来访问对象(如果对象还存在)。

示例

复制代码
#include <iostream>
#include <memory>

class Widget {
public:
    Widget() { std::cout << "Widget constructed\n"; }
    ~Widget() { std::cout << "Widget destroyed\n"; }
};

int main() {
    std::shared_ptr<Widget> sharedPtr = std::make_shared<Widget>();
    std::weak_ptr<Widget> weakPtr = sharedPtr;

    std::cout << "Shared count: " << sharedPtr.use_count() << std::endl; // 输出 1

    if (auto tempSharedPtr = weakPtr.lock()) { // 尝试从 weak_ptr 获取 shared_ptr
        std::cout << "Object is alive\n";
    } else {
        std::cout << "Object is destroyed\n";
    }

    sharedPtr.reset(); // 释放 shared_ptr 所有权

    if (auto tempSharedPtr = weakPtr.lock()) { // 再次尝试获取
        std::cout << "Object is alive\n";
    } else {
        std::cout << "Object is destroyed\n"; // 此时输出
    }

    return 0;
}

unique_ptr

unique_ptr 是一种智能指针,它提供对单一对象的独占所有权。这意味着 std::unique_ptr 实例拥有它所指向的对象,并且不允许复制该智能指针,只允许移动。当 std::unique_ptr 被销毁时,它所指向的对象也会被自动销毁。

特点

  • 独占所有权模型。
  • 不支持复制,只支持移动。
  • 自动释放所拥有的资源。
  • 轻量级,开销小。

示例

复制代码
#include <iostream>
#include <memory>

class Widget {
public:
    Widget() { std::cout << "Widget constructed\n"; }
    ~Widget() { std::cout << "Widget destroyed\n"; }
};

int main() {
    std::unique_ptr<Widget> widgetPtr = std::make_unique<Widget>();
    // std::unique_ptr<Widget> anotherPtr = widgetPtr; // 编译错误,不能复制
    std::unique_ptr<Widget> movedPtr = std::move(widgetPtr); // 移动是允许的
    return 0;
}
相关推荐
Yolo_TvT8 小时前
C++:析构函数
c++
threelab9 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
武器大师729 小时前
lv_binding_js 代码解读
开发语言·javascript·ecmascript
不知名的老吴9 小时前
线程的生命周期之线程“插队“
java·开发语言·python
Hello:CodeWorld10 小时前
C 风格变参 vs C++ 变参模板:核心区别与选型指南
c语言·c++·算法
kaikaile199510 小时前
数字全息图处理系统(C# 实现)
开发语言·c#
秋911 小时前
Go语言(Golang)开发工程师全景解析:岗位职责·语言优势与使用场景·各城市薪资·发展前景·高考志愿填报(2026版)
开发语言·golang·高考
huangdong_12 小时前
1688商品图片采集技术解析:登录态处理与SKU图自动分类
开发语言
搬砖魁首12 小时前
基础能力系列 - 多线程2 - 条件变量
c++·rust·条件变量·原子类型·线程同步互斥
chase_my_dream12 小时前
C++ + SLAM 高频面试问题整理
开发语言·c++·面试