前言:
了解 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 <iostream>
#include <memory>
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<<std::endl;
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() = 0make_share<>():制造share_ptr;
std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数,并 通过 use_count() 来查看一个对象的引用计数
std::unique_ptr
std::unique_ptr 是一种独占的智能指针,它禁止其他智能指针与其共享同一个对象
既然是独占,换句话说就是不可复制。但是,我们可以利用 std::move 将其转移给其他的 unique_ptr
代码如下:
#include<iostream>
#include<memory>
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)"<<std::endl;
}
int main()
{
std::unique_ptr<Foo>p1(std::make_unique<Foo>());
if(p1)p1->foo();
{
std::unique_ptr<Foo> 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<memory>
#include<iostream>
struct A;
struct B;
struct A {
std::shared_ptr<B> pointer;
~A() {
std::cout << "A 被销毁" << std::endl;
}
};
struct B {
std::shared_ptr<A> pointer;
~B() {
std::cout << "B 被销毁" << std::endl;
}
};
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->pointer = b;
b->pointer = a;
}
结果如图:
运行过程:
分析: 运行结果是 A, B 都不会被销毁,这是因为 a,b 内部的 pointer 同时又引用了 a,b,这使得 a,b 的引 用计数均变为了 2,而离开作用域时,a,b 智能指针被析构,却只能造成这块区域的引用计数减一,这样 就导致了 a,b 对象指向的内存区域引用计数不为零,而外部已经没有办法找到这块区域了,也就造成了 内存泄露
可以应用
#include<memory>
#include<iostream>
struct A;
struct B;
struct A {
std::shared_ptr<B> pointer;
~A() {
std::cout << "A 被销毁" << std::endl;
}
};
struct B {
std::weak_ptr<A> pointer;
~B() {
std::cout << "B 被销毁" << std::endl;
}
};
int main() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->pointer = b;
b->pointer = a;
}
结果:
运行过程:
解决这个问题的办法就是使用弱引用指针 std::weak_ptr,std::weak_ptr 是一种弱引用(相比较 而言 std::shared_ptr 就是一种强引用)。弱引用不会引起引用计数增加
在上图中,最后一步只剩下 B,而 B 并没有任何智能指针引用它,因此这块内存资源也会被释放
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/129687.html