当前位置: 首页 > news >正文

通过对比理解C++智能指针

理论

  • 概述

    • 智能指针:把管理资源的责任交给了对象,这样我们在使用结束时不需要显式地释放资源,而是由析构函数自动完成这一工作

    • 它是一个类模板,可以创建任意类型的指针对象

    • 智能指针使用时,资源对象不能被重复释放,否则会导致未定义的行为

  • C++中的智能指针关系

    智能指针简述
    auto_ptr存在管理权转移问题,不适合共享资源,已被C++11弃用
    unique_ptr暴力处理auto_ptr的管理权转移问题,独占资源所有权,不允许拷贝和赋值操作(开销小)
    shared_ptr使用引用计数优雅解决auto_ptr的问题,但带来了循环引用bug(开销大)
    weak_ptr专注给shared_ptr打补丁,不增加引用计数
  • auto_ptr

    • 不足

      管理权转移,原对象拷贝给新对象时,原对象被置为nullptr,此时只有新对象指向这块资源空间

      此时如果再使用原来的对象,那么会出现未定义的行为(资源不能被重复释放)

  • unique_ptr

    • 原理

      针对auto_ptr在拷贝、赋值操作中的管理权转移问题,unique_ptr直接禁用了拷贝和赋值操作

      这样自然就避免了重复释放资源

    • 不足

      不能进行拷贝和赋值操作

      可以使用move把左值转成右值进行赋值操作,此时效果和auto_ptr一样

  • shared_ptr

    • 原理

      为了进行拷贝和赋值操作,shared_ptr采用了引用计数原理,记录有多少shared_ptr实例指向同一资源

      有几个实例对象计数值就是几,调用析构函数会将计数值减1,当计数为0时,才会释放资源

      引用计数指向的对象在堆上,保证了所有线程都能够访问该资源;

      shared_ptr引用计数通过原子操作实现,因此引用计数本身是线程安全的

      但是,如果多线程尝试修改引用计数时,需要用户手动进行同步、加锁等操作以确保对资源的访问是安全的

    • 不足

      存在循环引用问题,导致引用计数不能变成0

      两个类对象,二者通过shared_ptr智能指针互相引用,会增加引用计数

  • weak_ptr

    • 原理

      weak_ptr对象可以指向shared_ptr,但不会改变shared_ptr的引用计数

    • 不足

      仅用于解决shared_ptr的循环引用问题,它不能直接访问资源



实验

auto_ptr管理权转移

// auto_ptr管理权转移
#include <iostream>
#include <memory>
using namespace std;class Test {
public:Test(const string &name):obj_name(name){ std::cout << obj_name << "——构造函数.\n"; }~Test(){ std::cout << obj_name << "——析构函数.\n"; }
private:string obj_name;
};int main() {auto_ptr<Test> obj1(new Test("obj1")); auto_ptr<Test> obj2(new Test("obj2"));cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;cout << "\n执行 obj2 = obj1\n" << endl;obj2 = obj1;cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;return 0;
}
image-20240915171327248

unique_ptr独占资源

// unique_ptr独占资源
#include <iostream>
#include <memory>
using namespace std;class Test {
public:Test(const string &name):obj_name(name){ cout << obj_name << "——构造函数.\n"; }~Test(){ cout << obj_name << "——析构函数.\n"; }
private:string obj_name;
};int main() {unique_ptr<Test> obj1(new Test("obj1")); unique_ptr<Test> obj2(new Test("obj2"));cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;cout << "\n执行 obj2 = move(obj1)\n" << endl;// obj2 = obj1;obj2 = move(obj1);cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;return 0;
}
image-20240915171846314

shared_ptr共享资源

// shared_ptr共享资源
#include <iostream>
#include <memory>
using namespace std;class Test {
public:Test(const string &name):obj_name(name){ cout << obj_name << "——构造函数.\n"; }~Test(){ cout << obj_name << "——析构函数.\n"; }
private:string obj_name;
};int main() {shared_ptr<Test> obj1(new Test("obj1")); shared_ptr<Test> obj2(new Test("obj2"));cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;cout << "\n执行 obj2 = obj1\n" << endl;obj2 = obj1;cout << "obj1 address = " << obj1.get() << endl;cout << "obj2 address = " << obj2.get() << endl;return 0;
}
image-20240915172140224

shared_ptr循环引用

// shared_ptr循环引用
#include <iostream>
#include <memory>
using namespace std;class A;class B {
public:shared_ptr<A> a_ptr;B() { cout << "B类 构造函数" << endl; }~B() { cout << "B类 析构函数" << endl; }
};class A {
public:shared_ptr<B> b_ptr;A() { cout << "A类 构造函数" << endl; }~A() { cout << "A类 析构函数" << endl; }
};int main() {// shared_ptr<A> a = make_shared<A>();// shared_ptr<B> b = make_shared<B>();shared_ptr<A> a(new A());shared_ptr<B> b(new B());a->b_ptr = b;  // A 指向 Bb->a_ptr = a;  // B 指向 A// 循环引用导致对象不能被正确销毁cout << "a use_count: " << a.use_count() << endl;cout << "b use_count: " << b.use_count() << endl;return 0;
}
image-20240915173108183

weak_ptr解决循环引用

// weak_ptr解决循环引用
#include <iostream>
#include <memory>
using namespace std;class A;  // 前向声明class B {
public:weak_ptr<A> a_ptr;  // 使用 weak_ptr 来避免循环引用B() { cout << "B类 构造函数" << endl; }~B() { cout << "B类 析构函数" << endl; }
};class A {
public:// shared_ptr<B> b_ptr;weak_ptr<B> b_ptr;A() { cout << "A类 构造函数" << endl; }~A() { cout << "A类 析构函数" << endl; }
};int main() {// shared_ptr<A> a = make_shared<A>();// shared_ptr<B> b = make_shared<B>();shared_ptr<A> a(new A());shared_ptr<B> b(new B());a->b_ptr = b;b->a_ptr = a; cout << "a use_count: " << a.use_count() << endl;cout << "b use_count: " << b.use_count() << endl;return 0;
}
image-20240915173621393

http://www.mrgr.cn/news/27522.html

相关文章:

  • 面试常见题之Spring Cloud
  • 【数据库】MySQL内置函数
  • (k8s)Kubernetes本地存储接入
  • [C语言]第九节 函数一基础知识到高级技巧的全景探索
  • 【css】网页颜色设计没有灵感?看看我推荐的几调色个网站 吧
  • 使用python进行网络爬虫豆瓣影评
  • 分页查询标准流程
  • 本地不能訪問linux的kafka服務
  • 【物联网】一篇文章带你认识RFID(射频识别技术)
  • 【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】004 - Kernel 启动引导代码head.S 源码逐行分析
  • 白手套公司
  • supermap Iclient3d for cesium加载地形并夸大地形
  • SpringCloudAlibaba:Seata
  • 《C++代码高度优化之双刃剑:避免过度优化引发的“暗雷”》
  • 基于SSM的银发在线教育云平台的设计与实现
  • SpringCloud的学习,Consul服务注册与发现、分布式配置,以及 服务调用和负载均衡
  • JavaDS —— B树
  • 【测试报告】博客系统
  • 24年9月通信基础知识补充2
  • S32K3 工具篇7:如何使用VScode编译EB MCAL工程