了解 Objective-C/Swift 的程序员应该知道引用计数的概念。引用计数这种计数是为了防止内存泄 露而产生的。基本想法是对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那 么引用对象的引用计数就会增加一次,每删除一次引用,引用计数就会减一,当一个对象的引用计数减 为零时,就自动删除指向的堆内存。我们在构造函数的时候申请空间,而在析构函数(在 离开作用域时调用)的时候释放空间,也就是我们常说的 RAII 资源获取即初始化技术。
我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 new 和 delete 去『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序 员不再需要关心手动释放内存
std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除 显示的调用 delete,当引用计数变为零的时候就会将对象自动删除。
但还不够,因为使用 std::shared_ptr 仍然需要使用 new 来调用,这使得代码出现了某种程度上的 不对称。 std::make_shared 就能够用来消除显式的使用 new,所以 std::make_shared 会分配创建传入参 数中的对象,并返回这个对象类型的 std::shared_ptr 指针。例如
- #include
- #include
-
- void foo(std::shared_ptr<int>i)
- {
- (*i)++;
- }
- int main()
- {
- //auto pointer=new int(10) 非法,不能直接分配
- auto pointer=std::make_shared<int>(10);
- foo(pointer);
- std::cout<<*pointer<
-
- auto pointer1=pointer;
- auto pointer2=pointer;
- int *p=pointer.get();
- std::cout<<*p<<"\n";
- std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
- std::cout << "pointer1.use_count() = " << pointer1.use_count() << std::endl; // 3
- std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; //
- pointer2.reset();
- std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
- std::cout << "pointer1.use_count() = " << pointer1.use_count() << std::endl; // 3
- std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; //
-
-
- }
结果:
11
11
pointer.use_count() = 3
pointer1.use_count() = 3
pointer2.use_count() = 3
pointer.use_count() = 2
pointer1.use_count() = 2
pointer2.use_count() = 0
make_share<>():制造share_ptr;
std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数,并 通过 use_count() 来查看一个对象的引用计数
std::unique_ptr
std::unique_ptr 是一种独占的智能指针,它禁止其他智能指针与其共享同一个对象
既然是独占,换句话说就是不可复制。但是,我们可以利用 std::move 将其转移给其他的 unique_ptr
代码如下:
- #include
- #include
-
- struct Foo
- {
- Foo() { std::cout << "Foo::Foo" << std::endl; }
- ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
- void foo() { std::cout << "Foo::foo" << std::endl; }
- };
-
- void f(const Foo&)
- {
- std::cout<<"f(const Foo)"<
- }
- int main()
- {
- std::unique_ptr
p1(std::make_unique()); - if(p1)p1->foo();
-
- {
- std::unique_ptr
p2(std::move(p1)) ; - f(*p2);
- if(p2)p2->foo();
- if(p1)p1->foo();
- p1 = std::move(p2);
- // p2 为空, 无输出
- if(p2) p2->foo();
- std::cout << "p2 被销毁" << std::endl;
- }
- if (p1) p1->foo();
- }
Foo::Foo
Foo::foo
f(const Foo)
Foo::foo
p2 被销毁
Foo::foo
Foo::~Foo
疑点:为什么p1没有重新构建 ,p2没有销毁?
首先unique_ptr的目的是禁止复制,我们所用的是std::move,实现转移,所以对象一直存在(内存空间),离开实例时候消失。
std::weak_ptr
如果你仔细思考 std::shared_ptr 就会发现依然存在着资源无法释放的问题
- #include
- #include
- struct A;
- struct B;
- struct A {
- std::shared_ptr pointer;
- ~A() {
- std::cout << "A 被销毁" << std::endl;
- }
- };
- struct B {
- std::shared_ptr pointer;
- ~B() {
- std::cout << "B 被销毁" << std::endl;
- }
- };
- int main() {
- auto b = std::make_shared();
- a->pointer = b;
- b->pointer = a;
- }
结果如图:
运行过程:
分析: 运行结果是 A, B 都不会被销毁,这是因为 a,b 内部的 pointer 同时又引用了 a,b,这使得 a,b 的引 用计数均变为了 2,而离开作用域时,a,b 智能指针被析构,却只能造成这块区域的引用计数减一,这样 就导致了 a,b 对象指向的内存区域引用计数不为零,而外部已经没有办法找到这块区域了,也就造成了 内存泄露
可以应用
- #include
- #include
- struct A;
- struct B;
- struct A {
- std::shared_ptr pointer;
- ~A() {
- std::cout << "A 被销毁" << std::endl;
- }
- };
- struct B {
- std::weak_ptr pointer;
- ~B() {
- std::cout << "B 被销毁" << std::endl;
- }
- };
- int main() {
- auto b = std::make_shared();
- a->pointer = b;
- b->pointer = a;
- }
结果:
运行过程:
解决这个问题的办法就是使用弱引用指针 std::weak_ptr,std::weak_ptr 是一种弱引用(相比较 而言 std::shared_ptr 就是一种强引用)。弱引用不会引起引用计数增加
在上图中,最后一步只剩下 B,而 B 并没有任何智能指针引用它,因此这块内存资源也会被释放
-
相关阅读:
jmeter 压测数据库
矩阵的投影、线性拟合与最小二乘法
java面向对象之多态(向上转型和向下转型)(详细而精炼)
百度推出可24时直播带货的AI数字人
HTTP请求的几种方式
ZooKeeper 客户端API操作
我有一篇Java Stream使用手册,学了就是你的了!
Malloc技术原理解析以及在转转搜索业务上的实践
undefined reference to symbol ‘g_signal_connect_data‘
C Primer Plus(6) 中文版 第10章 数组和指针 10.3 指针和数组
-
原文地址:https://blog.csdn.net/qq_62309585/article/details/126753586