C++智能指针

auto_ptr

 

auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存!

用法:
头文件: #include < memory >
用 法: auto_ptr<类型> 变量名(new 类型)

例 如:
auto_ptr< string > str(new string(“我要成为大牛~ 变得很牛逼!”));
auto_ptr<vector< int >> av(new vector< int >());
auto_ptr< int > array(new int[10]);

get() 获取智能指针托管的指针地址

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp = test.get();		// 获取指针返回
cout << "tmp->debug:" << tmp->getDebug() << endl;

release() 取消智能指针对动态内存的托管

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp2 = test.release();	// 取消智能指针对动态内存的托管
delete tmp2;	// 之前分配的内存需要自己手动释放

reset() 重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉

// 定义智能指针
auto_ptr<Test> test(new Test);

test.reset();			// 释放掉智能指针托管的指针内存,并将其置NULL

test.reset(new Test());	// 释放掉智能指针托管的指针内存,并将参数指针取代之

auto_ptr 被C++11抛弃的主要原因

1). 复制或者赋值都会改变资源的所有权

2). 在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值

3). 不支持对象数组的内存管理

unique_ptr可以解决这些问题。

unique_ptr:独享指针

  1. 基于排他所有权模式:两个指针不能指向同一个资源
  2. 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
  3. 在 STL 容器中使用unique_ptr,不允许直接赋值
  4. 支持对象数组的内存管理
  • 第二点详解:
  • unique_ptr<string> p1(new string("I'm Li Ming!"));
    unique_ptr<string> p2(new string("I'm age 22."));
    	
    cout << "p1:" << p1.get() << endl;
    cout << "p2:" << p2.get() << endl;
    
    p1 = p2;					// 禁止左值赋值
    unique_ptr<string> p3(p2);	// 禁止左值赋值构造
    
    unique_ptr<string> p3(std::move(p1));
    p1 = std::move(p2);	// 使用move把左值转成右值就可以赋值了,效果和auto_ptr赋值一样
    
    cout << "p1 = p2 赋值后:" << endl;
    cout << "p1:" << p1.get() << endl;
    cout << "p2:" << p2.get() << endl;
    

    在这里插入图片描述

为了解决这样的问题,我们可以使用shared_ptr指针!

shared_ptr:共享指针

熟悉了unique_ptr 后,其实我们发现unique_ptr 这种排他型的内存管理并不能适应所有情况,有很大的局限!如果需要多个指针变量共享怎么办?

shared_ptr可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它!这就是 shared_ptr 采用的策略!

多个shared_ptr可以共享一个对象的所有权。当最后一个shared_ptr离开作用域时,内存才会自动释放。引用计数的概念,可以通过调用use_count()可以得到引用计数,当计数为0时,对象才能自动析构。

在使用shared_ptr智能指针时,要注意避免对象交叉使用智能指针的情况! 否则会导致内存泄露!

如下代码:
Boy类中有Girl的智能指针;
Girl类中有Boy的智能指针;
当他们交叉互相持有对方的管理对象时…

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 构造函数" << endl;
	}

	~Boy() {
		cout << "~Boy 析构函数" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;
	}

private:
	shared_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 构造函数" << endl;
	}

	~Girl() {
		cout << "~Girl 析构函数" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 陷阱用法
	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
	// 此时boy和girl的引用计数都是2
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

useTrap函数结束后,函数中定义的智能指针被清掉,boy和girl指针的引用计数减1,还剩下1,接下来要释放对象时,发现对象中还存在智能指针,指向Girl对象内存还有一个智能指针,指向Boy对象内存还有一个智能指针,所以函数结束后没有将Boy和Girl对象内存释放同时也没有将对象中的智能指针内存释放。

当然,这也是有办法解决的,那就是使用weak_ptr弱指针。

weak_ptr:弱指针

weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。当弱指针指向一个对象,不会增加指向这块内存的引用计数。

  1. 弱指针的使用;
    weak_ptr wpGirl_1; // 定义空的弱指针
    weak_ptr wpGirl_2(spGirl); // 使用共享指针构造
    wpGirl_1 = spGirl; // 允许共享指针赋值给弱指针

  2. 弱指针也可以获得引用计数;
    wpGirl_1.use_count()

  3. 弱指针不支持 * 和 -> 对指针的访问;

  4. 在必要的使用可以转换成共享指针 lock();

  5. 调用use-count()去获取引用计数,该方法只返回强引用计数,并不返回弱引用计数。
  6. 调用expired()方法。比调用use_count()方法速度更快。
  • 对象交叉使用智能指针问题解决

    • 由于weak_ptr并不会改变shared_ptr的引用计数,所以修改类A,和类B中的shared_ptr对象为weak_ptr对象即可释放资源。

    • 可以看到下面boy类中的智能指针使用的是弱指针,girl类中的智能指针使用的是共享指针。当弱指针指向一个对象,也不会增加指向这块内存的引用计数。因此Girl对象的引用计数为1,所以不会造成内存泄漏问题。

  1. #include <iostream>
    #include <string>
    #include <memory>
    
    using namespace std;
    
    class Girl;
    
    class Boy {
    public:
    	Boy() {
    		cout << "Boy 构造函数" << endl;
    	}
    
    	~Boy() {
    		cout << "~Boy 析构函数" << endl;
    	}
    
    	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
    		this->girlFriend = _girlFriend;
    
    
    		// 在必要的使用可以转换成共享指针
    		shared_ptr<Girl> sp_girl;
    		sp_girl = this->girlFriend.lock();
    
    		cout << sp_girl.use_count() << endl;
    		// 使用完之后,再将共享指针置NULL即可
    		sp_girl = NULL;
    	}
    
    private:
    	weak_ptr<Girl> girlFriend;
    };
    
    class Girl {
    public:
    	Girl() {
    		cout << "Girl 构造函数" << endl;
    	}
    
    	~Girl() {
    		cout << "~Girl 析构函数" << endl;
    	}
    
    	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
    		this->boyFriend = _boyFriend;
    	}
    
    private:
    	shared_ptr<Boy> boyFriend;
    };
    
    
    void useTrap() {
    	shared_ptr<Boy> spBoy(new Boy());
    	shared_ptr<Girl> spGirl(new Girl());
    
    	spBoy->setGirlFriend(spGirl);
    	spGirl->setBoyFriend(spBoy);
    }
    
    
    int main(void) {
    	useTrap();
    
    	system("pause");
    	return 0;
    }
    

    expired函数的用法

  • expired:判断当前weak_ptr智能指针是否还有托管的对象,有则返回false,无则返回true

    如果返回true,等价于 use_count() == 0,即已经没有托管的对象了;当然,可能还有析构函数进行释放内存,但此对象的析构已经临近(或可能已发生)。

    智能指针的使用陷阱

  • 不要把一个原生指针给多个智能指针管理;

    int *x = new int(10);
    unique_ptr< int > up1(x);
    unique_ptr< int > up2(x);
    // 警告! 以上代码使up1 up2指向同一个内存,非常危险
    或以下形式:
    up1.reset(x);
    up2.reset(x);

  • 记得使用u.release()的返回值;
    在调用u.release()时是不会释放u所指的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了.

  • 禁止delete 智能指针get 函数返回的指针;
    如果我们主动释放掉get 函数获得的指针,那么智能 指针内部的指针就变成野指针了,析构时造成重复释放,带来严重后果!

  • 禁止用任何类型智能指针get 函数返回的指针去初始化另外一个智能指针!
    shared_ptr< int > sp1(new int(10));
    // 一个典型的错误用法 shared_ptr< int > sp4(sp1.get());

  • 总结于C++ 智能指针 - 全部用法详解_cpp智能指针特性_cpp_learners的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值