0
点赞
收藏
分享

微信扫一扫

【C++11】Smart Pointer 智能指针


文章目录

  • ​​一、为啥使用智能指针呢​​
  • ​​二、shared_ptr智能指针​​
  • ​​三、unique_ptr智能指针​​
  • ​​四、weak_ptr智能指针​​
  • ​​五、智能指针怎么解决交叉引用,造成的内存泄漏​​
  • ​​5.1 交叉引用的栗子:​​
  • ​​5.2 解决方案​​
  • ​​六、智能指针的注意事项​​
  • ​​Reference​​

一、为啥使用智能指针呢

标准库中的智能指针:
std::auto_ptr --single ownership (C++98中出现,缺陷较多,被摒弃)
std::unique_ptr --single ownership (C++11替代std::auto_ptr,用于单线程)
std::shared_ptr --shared ownership (C++11,用于多线程)
std::weak_ptr --temp/no ownership (C++11)
Introduced in C++ 11
Defined in <memory> header.

首先看一个下面的栗子,左边是木有使用智能指针的情况,当执行​​foo()​​​函数,其中的​​e​​​指针会在​​bar(e)​​​时传入​​bar​​​函数,但是在​​bar​​​函数结束后没有人为​​delete e​​​时,就会导致内存泄漏;但是在右边的栗子中,使用了​​unique_ptr​​智能指针(single ownership),就能防止内存泄漏。

【C++11】Smart Pointer 智能指针_c++


智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏

  • ​auto_ptr​​智能指针:(C++11出来前只有这种智能指针)当对象拷贝或者赋值后,前面的对象就悬空了。
  • ​unique_ptr​​智能指针:防止智能指针拷贝和复制。
  • ​shared_ptr​​智能指针:通过引用计数的方式来实现多个​​shared_ptr​​对象之间共享资源。
  • ​weak_ptr​​​智能指针:可以从一个​​shared_ptr​​​或另一个​​weak_ptr​​对象构造,它的构造和析构不会引起引用记数的增加或减少。

注意:每一种智能指针都可以增加内存的引用计数。

  • 智能指针分为两类:
  • 一种是可以使用多个智能指针管理同一块内存区域,每增加一个智能指针,就会增加1次引用计数,
  • 另一类是不能使用多个智能指针管理同一块内存区域,通俗来说,当智能指针2来管理这一块内存时,原先管理这一块内存的智能指针1只能释放对这一块指针的所有权()。
  • 按照这个分类标准,​​auto_ptr​​​​unique_ptr​​​​weak_ptr​​​属于后者,​​shared_ptr​​属于前者。

对​​shared_ptr​​​进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过​​make_shared​​​函数或者通过构造函数传入普通指针。并可以通过​​get​​函数获得普通指针。

#include <string>
#include <memory>
using namespace std;
class report
{
private:
string str;
public:
report(const string s):str(s) //构造方法
{
cout<<"1 report Object has been build!"<<endl;
}
~report()
{
cout<<"3 report Object deleted!"<<endl;
}
void talk()
{
cout<<str<<endl;
}
};

int main()
{
string talk="2 hello,this is a test!";
{
auto_ptr<report> ptr(new report(talk));
ptr->talk();
}
{
shared_ptr<report> ptr(new report(talk));
ptr->talk();
}
{
unique_ptr<report> ptr(new report(talk));
ptr->talk();
}
return 0;
}

【C++11】Smart Pointer 智能指针_引用计数_03

二、shared_ptr智能指针

​shared_ptr​​实现了共享拥有的概念,利用“引用计数”来控制堆上对象的生命周期。

​share_ptr​​的生命周期:

【C++11】Smart Pointer 智能指针_智能指针_04


原理:在初始化的时候引用计数设为1,每当被拷贝或者赋值的时候引用计数+1,析构的时候引用计数-1,直到引用计数被减到0,那么就可以delete掉对象的指针了。他的构造方式主要有以下三种:

shared_ptr<Object> ptr;
shared_ptr<Object> ptr(new Object);
shared_ptr<Object> ptr(new Object, [=](Object *){ //回收资源时调用的函数 });
auto ptr = make_shared<Object>(args);

  • 第一种空构造,没有指定​​shared_ptr​​管理的堆上对象的指针,所以引用计数为0,后期可以通过​​reset()​​成员函数来指定其管理的堆上对象的指针,​​reset()​​之后引用计数设为1。
  • 第二种是比较常见的构造方式,构造函数里面可以放堆上对象的指针,也可以放其他的智能指针(如​​weak_ptr​​)。
  • 第三种构造方式指定了​​shared_ptr​​在析构自己所保存的堆上对象的指针时(即引用计数为0时)所要调用的函数,这说明我们可以自定义特定对象的特定析构方式。同样的,​​reset()​​成员函数也可以指定析构时调用的指定函数。
  • 第四种方法:较常见,构造​​shared_ptr​​的方式(最安全):

auto ptr = make_shared<Object>(args);

上面第四种方法,使用标准库里边的​​make_shared<>()​​​模板函数。该函数会调用模板类的构造方法,实例化一个堆上对象,然后将保存了该对象指针的​​shared_ptr​​​返回。参数是该类构造函数的参数,所以使用​​make_shared<>()​​​就好像单纯地在构造该类对象一样。​​auto​​​是C++11的一个关键字,可以在编译期间自动推算变量的类型,在这里就是​​shared_ptr<Object>​​类型。



【C++11】Smart Pointer 智能指针_智能指针_05


​shared_ptr​​的其他成员函数:

use_count()  //返回引用计数的个数

unique() //返回是否是独占所有权(use_count是否为1)

swap() //交换两个shared_ptr对象(即交换所拥有的对象,引用计数也随之交换)

reset() //放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少

三、unique_ptr智能指针

注意​​unique_ptr​​是single ownership的,不能拷贝。其构造方式如下:

【C++11】Smart Pointer 智能指针_内存泄漏_06


​unique_ptr​​的生命周期:

【C++11】Smart Pointer 智能指针_智能指针_07

四、weak_ptr智能指针

【C++11】Smart Pointer 智能指针_内存泄漏_08

五、智能指针怎么解决交叉引用,造成的内存泄漏

结论:创建对象时使用​​shared_ptr​​​强智能指针指向,其余情况都使用​​weak_ptr​​弱智能指针指向。

5.1 交叉引用的栗子:

当A类中有一个指向B类的​​shared_ptr​​​强类型智能指针,B类中也有一个指向A类的​​shared_ptr​​​强类型智能指针。
​​​main​​函数执行后有两个强智能指针指向了对象A,对象A的引用计数为2,B类也是:

#include <iostream>
#include <memory>
using namespace std;

class B;
class A{
public:
shared_ptr<B> _bptr;
};

class B{
public:
shared_ptr<A> _aptr;
};

int main(){
shared_ptr<A> aptr(new A());
shared_ptr<B> bptr(new B());
aptr->_bptr = bptr;
bptr->_aptr = aptr;
return 0;
}

【C++11】Smart Pointer 智能指针_引用计数_09


而当主函数​​main​​​的​​return​​​返回后,对象​​A​​​的引用计数减一变为1(​aptr没指向​A​对象了),​​B​​对象也是,引用计数不为0,即不能析构2个对象释放内存,造成内存泄漏。

5.2 解决方案

将类A和类B中的​​shared_ptr​​​强智能指针都换成​​weak_ptr​​弱智能指针;

class A{
public:
weak_ptr<B> _bptr;
};
class B{
public:
weak_ptr<A> _aptr;
};

​weak_ptr​​弱智能指针,虽然有引用计数,但实际上它并不增加计数,而是只观察对象的引用计数。所以此时对象A的引用计数只为1,对象B的引用计数也只为1。

【C++11】Smart Pointer 智能指针_引用计数_10

六、智能指针的注意事项

  • 避免同一块内存绑定到多个独立创建的shared_ptr上,因此要不使用相同的内置指针初始化(或reset)多个智能指针,不要混合使用智能指针和普通指针,坚持只用智能指针。
  • 不delete get() 函数返回的指针,因为这样操作后,​​shared_ptr​​并不知道它管理的内存被释放了,会造成shared_ptr重复析构。
  • 不使用 get()函数初始化或(reset)另外的智能指针。

shared_ptr<int> p = make_share<int> (42);
int *q = p.get();
{
shared_ptr<int>(q);
} // 程序块结束,q被销毁,指向的内存被释放。
int foo = *p; // 出错,p指向的内存已经被q释放,这是用get() 初始化另外的智能指针惹得祸。
// 请记住,永远不要用get初始化另外一个智能指针。

  • 能使用​​unique_ptr​​​时就不要使用​​share_ptr​​指针(后者需要保证线程安全,所以在赋值or销毁时overhead开销更高)。

Reference


举报

相关推荐

0 条评论