C++智能指针

智能指针

解决常规指针在销毁时为释放指向的变量而导致内存泄露的问题, 因此对常规指针进行了封装, 利用析构函数来对指针指向的内存进行释放.

智能指针分类

在stl里面有四种智能指针, auto_ptr、unique_ptr、shared_ptr和weak_ptr. 需要使用智能指针需要先包含头文件(需注意, 这些智能指针的构造函数都用explicit声明, 所以不能用隐式转换的方式进行初始化):

#include <memory>

auto_ptr

auto_ptr在C++ 98开始被使用, C++11被抛弃.

内部实现

auto_ptr对其封装的指针是独占的, 在其内部通过重载了拷贝构造函数和赋值运算符, 在一个拥有指针p的auto_ptr aP1通过拷贝构造函数赋给了另一个aP2之后, aP1对于p的所有权就交给了aP2

代码实现

auto_ptr是C++98开始使用的, 如果有以下代码:

class Test
{
    public: 
    Test(int a = 0 ) : m_a(a) { }
    ~Test( )
    { 
       cout << "Calling destructor" << endl; 
    }
    public: int m_a;
};
void main( )
{ 
    std::auto_ptr<Test> p( new Test(5) ); 
    cout << p->m_a << endl;
}  

上述代码在堆区申请申请了一个Test大小的内存并让智能指针指向这块内存, 而智能指针在析构的时候会自动释放掉这段内存.

不足

  • 当一个auto_ptr aP1指向了资源A, 在用aP1给aP2赋值, 这样aP2就拥有了资源A且aP1就不会再拥有, 这样使用aP1就是有风险的, 且其作为函数参数传递也会转移其所有权.
  • 不能指向对象数组, 不能指向一组对象
  • 不能同标准容器一块儿使用, C++的STL容器对于容器元素类型的要求是有值语义,即可以赋值和复制。auto_ptr在赋值和复制时都进行了特殊操作,所以auto_ptr对象不能作为STL容器元素。

现在基本已经不使用auto_ptr.

unique_ptr

构造

由于auto_ptr的一些不足, 所以有unique_ptr, unique_ptr构造函数不支持复制构造, 也不支持隐式构造. 同样, 该指针也是对其拥有资源的独占.

所有权转移操作

std::unique_ptr<int> uP1(new int(5))          //构造一个指向资源5的unique_ptr
unique_ptr<int> uP2 = std::move(uP1)          //转移资源5的所有权至uP2, 转移后uP1
                                              //就不能继续使用

操作汇总

unique_ptr<ele_type, del_type> uP(del)                 //del为del_type类型的删除器
                                                       //这样可以指定删除器

uP.release()                                           //接触对于当前资源的控制权并返
                                                       //回其所有的指针, 记得接收返回
                                                       //否则会造成内存泄露

uP.reset()                                             //解除对于当前资源的拥有权并指向
                                                       //指向参数, 同时原有资源释放掉

作为函数参数传递

虽然unique_ptr不能进行拷贝和赋值, 但是其作为函数内部变量快被销毁时可以作为返回值(编译器会自动调用std::move).

shared_ptr

C++ 智能指针底层是采用引用计数的方式实现的。简单的理解,智能指针在申请堆内存空间的同时,会为其配备一个整形值(初始值为 1),每当有新对象使用此堆内存时,该整形值 +1;反之,每当使用此堆内存的对象被释放时,该整形值减 1。当堆空间对应的整形值为 0 时,即表明不再有对象使用它,该堆空间就会被释放掉。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。

构造

可以使用:

std::shared_ptr<int> p3 = std::make_shared<int>(10);

的方式构造一个智能指针. 

举例说明:

int main()
{
	string *s1 = new string("s1");

	shared_ptr<string> ps1(s1);
	shared_ptr<string> ps2;
	ps2 = ps1;

	cout << ps1.use_count()<<endl;	//2
	cout<<ps2.use_count()<<endl;	//2
	cout << ps1.unique()<<endl;	//0

	string *s3 = new string("s3");
	shared_ptr<string> ps3(s3);

	cout << (ps1.get()) << endl;	//033AEB48
	cout << ps3.get() << endl;	//033B2C50
	swap(ps1, ps3);	//交换所拥有的对象
	cout << (ps1.get())<<endl;	//033B2C50
	cout << ps3.get() << endl;	//033AEB48

	cout << ps1.use_count()<<endl;	//1
	cout << ps2.use_count() << endl;	//2
	ps2 = ps1;
	cout << ps1.use_count()<<endl;	//2
	cout << ps2.use_count() << endl;	//2
	ps1.reset();	//放弃ps1的拥有权,引用计数的减少
	cout << ps1.use_count()<<endl;	//0
	cout << ps2.use_count()<<endl;	//1
}

2.4 weak_ptr

share_ptr还是有一种情况会发生内存泄露,也就是两个share_ptr相互引用的时候,如以下代码:

class B;

class A {
public:
    share_ptr<B> _pb;
    ~A(){
        printf("A delete\n");
    }
};

class B {
public:
    share_ptr<A> _pa;
    ~B(){
        printf("B delete\n");
    }
};

int main() {
    share_ptr<A> spA(new A());
    share_ptr<B> spB(new B());
    cout << spA.use_count() << endl;    // 1
    cout << spB.use_count() << endl;    // 1
    spA._pb = spB;
    cout << spB.use_count() << endl;    // 2
    spB._pa = spA;
    cout << spA.use_count() << endl;    // 2
    return 0;
}

函数是这样的,share_ptr spA指向一个新的A对象,假设为objA,那么此时objA的引用计数为1次。share_ptr spB指向一个新的B对象,假设为objB,那么此时objB的引用计数为1。让spA._pb = spB,也就是让spA._pb也指向objB,那么此时objB的引用计数为2。让spB._pa = spA,也就是让spB._pa也指向objA,那么此时objA的引用计数也为2。接下来进行析构,发现此时栈上从底至定分别为spA,spB,那么先进行spB的析构,spB指向objB的引用计数为2,减1为1(此时objA的_pb还引用着),不释放。再进行spA的析构,spA指向objA的引用计数也为2,减1为1,也不释放,这样退出函数后,堆上面的objA和objB都没有释放,违背了share_ptr设计的初衷。

可以通过把其中一个类中的share_ptr改为weak_ptr解决以上问题。

weak_ptr是用来配合share_ptr工作的,可以用share_ptr对其赋值,赋值后weak_ptr同样指向share_ptr指向的对象,但是不能像share_ptr一样直接对对象进行操作,必须通过weak_ptr成员函数,返回一个share_ptr,才能进行操作。也可以用另一个weak_ptr对其进行赋值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值