智能指针-选择正确的智能指针

C++忘了学,学了忘,但是从未做过真正的项目,那为什么还会重复开头的场景呢?

因为一名Android工程师,C++是必备的技能,每一个产品,仔细去分析的时候都能发现很多模块都可以使用C++开发,从安全性、性能等方面都有帮助。

之前对于C/C++的影响就是指针难学,概念多,还有各种内存问题,着实很烦。

智能指针,用于管理动态分配内存,以帮助避免内存泄漏和管理资源的生命周期。智能指针是与原始指针不同的,它们可以自动地跟踪对象的所有权,当对象不再需要时,自动释放内存。 就问你怕不怕。

智能指针分类

在C++ 标准库中,有四个只能指针 即

  1. std::auto_ptr
  2. std::unique_ptr
  3. std::shared_ptr
  4. std::weak_ptr

其中std::auto_ptr在C++ 98 标准中就存在,目前已经废弃,所以不做学习。

std::unique_ptr

std::unique_ptr是C++11引入的独占所有权智能指针。它允许一个对象拥有另一个对象的唯一所有权,当std::unique_ptr超出作用域时,它会自动释放对象的内存。

c++ 复制代码
#include <memory>

std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);

主要特点:

  1. 唯一所有权:每个std::unique_ptr实例拥有对象的唯一所有权。
  2. 高效且安全:没有额外的引用计数开销,适用于大多数情况。
  3. 可移动语义:可以使用std::move转移所有权。
  4. 不能共享:不能复制或共享所有权,因此不会导致资源泄漏

std::shared_ptr

std::shared_ptr是C++11引入的共享所有权智能指针。它允许多个std::shared_ptr实例共享同一个对象的所有权,当最后一个std::shared_ptr超出作用域时,对象的内存将被释放。

c++ 复制代码
#include <memory>

std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(42);
std::shared_ptr<int> sharedPtr2 = sharedPtr1; // 共享所有权

主要特点:

  1. 共享所有权:多个std::shared_ptr可以共享同一个对象的所有权。
  2. 引用计数:内部维护引用计数,自动释放对象。
  3. 适用于循环引用:可以与std::weak_ptr一起用于解决循环引用问题。
  4. 有一定开销:引用计数可能引入一些额外的开销。

std::weak_ptr

std::weak_ptr也是C++11引入的,用于解决std::shared_ptr可能导致的循环引用问题。std::weak_ptr允许你观察std::shared_ptr管理的对象,但不拥有它。

c++ 复制代码
#include <memory>

std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr; // 弱引用

主要特点:

  1. 弱引用:不拥有对象,仅用于观察std::shared_ptr
  2. 避免循环引用:可以用于打破std::shared_ptr之间的循环引用,以防止内存泄漏。

使用推荐

通常推荐使用std::unique_ptr来管理独占所有权的资源,使用std::shared_ptrstd::weak_ptr来管理共享所有权的资源和解决循环引用问题。避免使用已弃用的std::auto_ptr

shared_str -- week_ptr

weak_ptr这个智能指针是用来辅助shared_ptr工作的

在上面智能指针的介绍中,shared_ptr 是共享所有权智能指针,它的核心点在于共享,存在引用计数逻辑,在下面这种场景中,可能导致循环引用问题

c++ 复制代码
#include <memory>

class A;
class B;

class A {
public:
    // 使用共享所有权智能指针
    std::shared_ptr<B> b_ptr;

    A() {
        std::cout << "A constructor" << std::endl;
    }

    ~A() {
        std::cout << "A destructor" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a_ptr;

    B() {
        std::cout << "B constructor" << std::endl;
    }

    ~B() {
        std::cout << "B destructor" << 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;
}

在上面的示例中,类 AB 分别包含一个 std::shared_ptr,它们相互引用对方。当 ab 的引用计数都不再为零时,它们的析构函数永远不会被调用,导致内存泄漏。

为了解决这个问题,可以使用 std::weak_ptr 来打破循环引用。std::weak_ptr 允许你观察 std::shared_ptr 管理的对象,但不拥有它,因此不会增加引用计数

以下是使用 std::weak_ptr 解决循环引用问题的代码

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

class A;
class B;

class A {
public:
    // 使用 weak_ptr 代替 shared_ptr
    std::weak_ptr<B> b_weak_ptr;

    A() {
        std::cout << "A constructor" << std::endl;
    }

    ~A() {
        std::cout << "A destructor" << std::endl;
    }
};

class B {
public:
    // 使用 weak_ptr 代替 shared_ptr
    std::weak_ptr<A> a_weak_ptr;

    B() {
        std::cout << "B constructor" << std::endl;
    }

    ~B() {
        std::cout << "B destructor" << std::endl;
    }
};

int main() {
    // 创建 shared_ptr
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    // 使用 weak_ptr
    a->b_weak_ptr = b;
    b->a_weak_ptr = a;


    // 当 a 和 b 的引用计数为零时,它们的析构函数会被调用,不会出现内存泄漏

    return 0;
}

std::weak_ptr 用于替代了 std::shared_ptr,并且在 main 函数的末尾显式地释放了 abshared_ptr,使它们的引用计数减少

对于 Java语言型选手,这个疑虑要消除

std::shared_ptr 和Java中的GC Roots(垃圾收集根节点)确实有一些相似之处,当看到引用计数这四个字时,肯定会想到GC roots 的算法。

我们来对比一下:

相似之处:

  1. 引用计数std::shared_ptr 和GC Roots 都与引用计数相关。std::shared_ptr 使用引用计数来跟踪有多少个智能指针实例引用了同一对象。GC Roots 也类似,它们是根节点,跟踪哪些对象被引用,哪些不再被引用。
  2. 生命周期管理std::shared_ptr 和GC Roots 都用于管理对象的生命周期。std::shared_ptr 通过引用计数来确保对象在没有引用时被释放,GC Roots 确保被引用的对象不会被垃圾收集器回收。
  3. 循环引用问题std::shared_ptr 和GC Roots 都可以解决循环引用问题。在std::shared_ptr 中,使用 std::weak_ptr 来打破循环引用,GC Roots 可以识别不再可达的对象并释放它们。

不同之处

  1. 语言和环境std::shared_ptr 是C++中的一个智能指针,用于管理动态分配的内存。GC Roots 是Java虚拟机中的概念,用于垃圾回收。它们存在于不同的编程语言和执行环境中。
  2. 垃圾收集方式 :GC Roots 通常与垃圾回收算法(如标记-清除、分代垃圾回收)一起使用,以识别和收集不再可达的对象。而 std::shared_ptr 使用引用计数来跟踪对象的引用情况,不需要像垃圾回收器那样扫描整个对象图。
  3. 性能和开销 :由于不同的垃圾回收算法和环境要求,GC Roots 可能会引入一些性能开销,而 std::shared_ptr 的开销通常较小。

总结

在C++编程中,智能指针是一种强大的工具,可用于管理内存和资源,确保安全和高效的资源管理。std::shared_ptrstd::unique_ptr是现代C++中常用的智能指针类型,分别用于共享和独占资源所有权,在以后的开发中可以放心大胆的使用。

相关推荐
无夜_17 分钟前
Prototype(原型模式)
开发语言·c++
刘好念36 分钟前
[图形学]smallpt代码详解(1)
c++·计算机图形学
fpcc40 分钟前
并行编程实战——TBB框架的应用之一Supra的基础
c++·并行编程
兵哥工控40 分钟前
MFC工控项目实例二十二主界面计数背景颜色改变
c++·mfc
兵哥工控43 分钟前
MFC工控项目实例二十手动测试界面模拟量输入实时显示
c++·mfc
jyan_敬言1 小时前
【Linux】Linux命令与操作详解(一)文件管理(文件命令)、用户与用户组管理(创建、删除用户/组)
linux·运维·服务器·c语言·开发语言·汇编·c++
笑非不退2 小时前
C++ 异步编程 并发编程技术
开发语言·c++
T0uken2 小时前
【QT Qucik】C++交互:接收QML信号
c++·qt·交互
爱写代码的刚子2 小时前
C++知识总结
java·开发语言·c++
s_little_monster3 小时前
【QT】QT入门
数据库·c++·经验分享·笔记·qt·学习·mfc