智能指针 同一个

2 篇文章 0 订阅

一 C++提供的智能指针总结

为什么需要智能指针?

1)当抛出异常时,我们可能会忘记释放new的指针,导致内存泄漏,或者说较大的工程中,普通情况也会忘记析构指针

2)出现两个指针指向同一块内存时,如果释放时处理不好,会出现释放一个内存两次,或者出现悬垂指针

3)更不用说深拷贝和前拷贝的问题了

所以提出了如下几种智能指针

auto_ptr

指针的实现:

template< class T>
class my_auto_ptr 
{
private:
	T* m_ptr;
	T* GetPtr()
	{ //供构造赋值时使用
		T* tmp = m_ptr;
		m_ptr = 0;
		return tmp;
	}
public:
	explicit my_auto_ptr( T* p = 0) :m_ptr( p ) { }
	~my_auto_ptr() { delete m_ptr; }
	T& operator*() { return *m_ptr;}
	T* operator->() { return m_ptr;}
	my_auto_ptr(my_auto_ptr& mp)
	{   //复制构造函数
		m_ptr = mp.GetPtr(); //mp复制过来后它自己原来的指针相当于失效了.
	}

	my_auto_ptr& operator=(my_auto_ptr& ap)
	{ 
		if(ap != *this)
		{
			delete m_ptr;
			m_ptr = ap.GetPtr();
		}
		return *this;
	}
	void reset(T* p)
	{  //指针重置,相当于把指针指向另外一个地方去
		if(p != m_ptr)
			delete m_ptr;
		m_ptr = p;
	}
};

auto_ptr的缺陷

上面我实现的my_auto_ptr基本上是实现了auto_ptr的所有核心功能.从里面我们可以明显的看到一个很大缺陷.我们看到当通过复构造函数,通过操作符=赋值后,原来的那个智能指针对象就失效了.只有新的智能指针对象可以有效使用了.用个专业点的说法叫所有权的转移.被包装的指针指向的内存块就像是一份独享占用的财产,当通过复制构造,通过=赋值传给别的智能指针对象时,原有的对象就失去了对那块内存区域的拥有权.也就是说任何时候只能有一个智能指针对象指向那块内存区域,不能有两个对象同时指向那块内存区域.

这样一来auto_ptr不能做为STL中容器的元素,为啥呢? 因为容器中经常存在值拷贝的情况嘛,把一个容器对象直接赋值给另一对象.完了之后两个容器对象可得都能用啊.而如果是auto_ptr的话显然赋值后只能一个有用,另一个完全报废了.另外比如你有个变量auto_ptr<int> ap( new int(44) );  然后ap被放进一个容器后,ap就报废不能用了.

不过没办法啊,在c++ 11标准之前,现在我们大部分用的是98标准吧,STL里面只有auto_ptr这一种智能指针.而在11标准中除了auto_ptr还有如下三种:

unique_ptr

smart pointer with unique object ownership semantics

只能有一个主人的指针,可以用于STL容器

shared_ptr

smart pointer with shared object ownership semantics

可共享的指针

weak_ptr

weak reference to an object managed by std::shared_ptr

弱引用指针

unique_ptr

//智能指针的创建  
unique_ptr<int> u_i; //创建<span style="font-family:Arial,Helvetica,sans-serif">“</span><span style="font-family:Arial,Helvetica,sans-serif">空智能指针”</span>  
u_i.reset(new int(3)); //"绑定”动态对象  
unique_ptr<int> u_i2(new int(4));//创建时指定动态对象  
//所有权的变化  
int *p_i = u_i2.release(); //释放所有权  
unique_ptr<string> u_s(new string("abc"));  
unique_ptr<string> u_s2 = std::move(u_s); //所有权转移(通过移动语义),u_s所有权转移后,变成“空指针”  
u_s2=nullptr;//显式销毁所指对象,同时智能指针变为空指针。与u_s2.reset()等价  
 可放在容器中(弥补了auto_ptr不能作为容器元素的缺点)

方式一:

[cpp]  view plain copy
  1. vector<unique_ptr<string>> vs { new string{“Doug”}, new string{“Adams”} };  
方式二:

[cpp]  view plain copy
  1. vector<unique_ptr<string>>v;  
  2. unique_ptr<string> p1(new string("abc"));  
  3. v.push_back(std::move(p1));//这里需要显式的移动语义,因为unique_ptr并无copy语义  

(4) 管理动态数组,因为unique_ptr有unique_ptr<X[]>重载版本,销毁动态对象时调用delete[]

[cpp]  view plain copy
  1. unique_ptr<int[]> p (new int[3]{1,2,3});  
  2. p[0] = 0;// 重载了operator[]  

unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象

auto_ptr有拷贝语义,拷贝后源对象变得无效;unique_ptr则无拷贝语义,但提供了移动语义

auto_ptr不可作为容器元素,unique_ptr可以作为容器元素

auto_ptr不可指向动态数组(尽管不会报错,但不会表现出正确行为),unique_ptr可以指向动态数组

总结:

从上面的例子可以看出,unique_ptr和auto_ptr真的非常类似.其实你可以这样简单的理解,auto_ptr是可以说你随便赋值,但赋值完了之后原来的对象就不知不觉的报废.搞得你莫名其妙.而unique就干脆不让你可以随便去复制,赋值.如果实在想传个值就哪里,显式的说明内存转移std:move一下.然后这样传值完了之后,之前的对象也同样报废了.只不过整个move你让明显的知道这样操作后会导致之前的unique_ptr对象失效.

3shared_ptr

实现原理:

#include <iostream>
using namespace std;
template<class T>
class shared_ptr
{
private:
	T* m_ptr; //被封装的指针
	unsigned int shared_count;   //引用计数,表示有多少个智能指针对象拥有m_ptr指向的内存块
public:
	shared_ptr(T* p):m_ptr(p),shared_count(1){ }
	~shared_ptr() { deconstruct();}
	void deconstruct()
	{
		if(shared_count == 1)   //引用计数为1表示只有一个对象使用指针指向的内存块了
		{
			delete m_ptr;
			m_ptr = 0;
		}
		shared_count--;
	}
	T& operator*() { return *m_ptr;}
	T* operator->() { return m_ptr;}
	//复制构造函数
	shared_ptr(shared_ptr& sp):m_ptr(sp.m_ptr),shared_count(sp.shared_count)
	{
		shared_count++;
	}
	//重载运算符=
	shared_ptr& operator = (shared_ptr& sp)
	{

		sp.shared_count++;   
		deconstruct();  //相当于先删掉左值,然后再通过右值赋值.
		m_ptr = sp.m_ptr;
		shared_count = sp.shared_count;
		return *this;
	} 
};
struct Arwen
{
	int age;
	Arwen(int gg) :age(gg) { };
};

//下面代码全部运行正确

void main()
{
	shared_ptr<Arwen> myPtr( new Arwen(24) );
	int num =myPtr->age;
	shared_ptr<Arwen> ptrOne( myPtr); //复制构造
	num =myPtr->age; //如果是auto_ptr该处会出错.因为把myPtr复制给ptrOne后,它自己本身相当于失效了
	num = ptrOne->age;
	shared_ptr<Arwen> ptrTwo = ptrOne;
	num = ptrOne->age;//如果是auto_ptr该处也会出错,此时ptrOne也失效了
	num = ptrTwo->age;
	return ;
}
  使用shared_ptr需要注意的问题

1)不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,如下所示:

    int *p5 = new int;
    std::shared_ptr<int> p6(p5);
    std::shared_ptr<int> p7(p5);// logic error
1、创建shared_ptr实例

最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share_ptr实例。如果你不想使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数。

示例如下:

int main() {

    // 传递给make_shared函数的参数必须和shared_ptr所指向类型的某个构造函数相匹配
    shared_ptr<string> pStr = make_shared<string>(10, 'a');
    cout << *pStr << endl;  //  aaaaaaaaaa

    int *p = new int(5);
    shared_ptr<int> pInt(p);
    cout << *pInt << endl;  // 5
}
2 拷贝和赋值操作

我们可以用一个shared_ptr对象来初始化另一个share_ptr实例,该操作会增加其引用计数值。

shared_ptr < string > pStr2(pStr);

如果shared_ptr实例p和另一个shared_ptr实例q所指向的类型相同或者可以相互转换,我们还可以进行诸如p = q这样赋值操作。该操作会递减p的引用计数值,递增q的引用计数值。

4 weak_ptr

http://blog.csdn.net/Xiejingfa/article/details/50772571

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值