智能指针

前景摘要

1)在复制控制里面,一般复制成员都是复制其值,复制指针只复制指针的地址,而不会复制指针指向的对象。

2) 将一个指针复制到另一个指针时,两个指针指向同一对象,删除其中任意一个,都对另一个有硬性,特别是动态分配的内存,会导致程序崩溃。

3) 如何使用指针呢?这里就牵涉几种情况:

  a)指针成员采取常规指针行为 :指针的缺陷但无需特殊的复制控制

  b)类采取智能指针行为:指针指向的对象是共享的,但能防止悬垂指针

  c)类采取值型行为:指针指向的对象时唯一的,由每个类对象单独管理

简单类的使用

如下一个普通的指针类,包含两个成员,一个int型指针,一个int型的普通变量:

class HasPtr
{
public:
	HasPtr(void);
	~HasPtr(void);

	HasPtr(int *p, int i):ptr(p), val(i) { };
	//HasPtr(const HasPtr&);
	//HasPtr& operator=(const HasPtr&);
	
	int* GetPtr() const { return ptr; }
	int GetVal() const { return val; }

	int SetPtr(int *p) { ptr = p; return 0; }
	int SetVal(int i) { val = i; return 0; }

	int GetPtrVal() const { return *ptr; }
	int SetPtrVal(int val) { *ptr = val;  return 0;}

private:
	int *ptr;
	int val;
};
如下调用:

	int nObj = 100;
	HasPtr p1, p2;
	p1 = HasPtr(&nObj, 10); // 复制构造函数和复制操作符都被调用
	p2 = HasPtr(p1);
	HasPtr p3 = HasPtr(&nObj, 10);	 //只发生了复制构造函数的调用
	HasPtr p4 = HasPtr(p3);

	p1.SetVal(2);
	cout << p1.GetVal() << " " << p2.GetVal() << endl;
	p1.SetPtrVal(18);
	cout << p1.GetPtrVal() << " " << p2.GetPtrVal() << endl;
其中val的值相对于类对象时独立的,而指针却是共享的,就会出现下面的悬垂指针,如下

	int *ip = new int(189);
	HasPtr ptr(ip, 123);
	delete ip;
	ip = NULL;
	ptr.SetPtrVal(124); //此时指针对象已经被释放
最后一个在设置之前,ptr就应经被释放了,这时候再赋值就找不到内存空间了

智能指针的使用

如果管理指针,如果直接控制有问题,那我们就间接控制,将指针绑定与一个单独的类,而使用类对象指针进行操作,主要增加使用计数count,来判定多少个对象还在使用资源,智能指针的设计如下

class U_Ptr
{
	friend class HasPtr;
	U_Ptr(int *p = NULL): ip(p), count(1) { };
	//U_Ptr(const U_Ptr& uptr);
	//U_Ptr& operator=(const U_Ptr& uptr);
	~U_Ptr() { }//delete ip; 释放一个不存在的指针 

private:
	int *ip;
	size_t count;
};
那这里也需要重新设计包含指针的类了,必须先更改其构造函数,让指针从属于类,由类自己自由操纵,所以设为动态分配;复制构造函数和赋值操作符函数也要重新设计,复制构造函数分配新元素并从被复制对象处复制值,赋值操作符撤销所保存的原对象并从右操作数向左操作数复制值;而析构函数就需要释放构造函数分配的资源。如下:
class HasPtr
{
public:
	HasPtr(void);
	~HasPtr(void);

	HasPtr(int *p, int i);
	HasPtr(const HasPtr&);
	HasPtr& operator=(const HasPtr&);
	
	int* GetPtr() const { return ptr->ip; }
	int GetVal() const { return val; }

	int SetPtr(int *p) { ptr->ip = p; return 0; }
	int SetVal(int i) { val = i; return 0; }

	int GetPtrVal() const { return *ptr->ip; }
	int SetPtrVal(int val) { *ptr->ip = val;  return 0;}

private:
	U_Ptr *ptr;
	int val;
};

其他函数的设计不予说明,如下

构造函数:

HasPtr::HasPtr(int *p, int i)
	:ptr(new U_Ptr(p)), val(i)
{
	cout << "two parameter constructor" << endl;
}
动态分配了一个新的堆内存,并将指针赋予ptr,这样ptr就间接指向了ip,而类对象就利用这个指针类类操纵ip了

复制构造函数:

HasPtr::HasPtr(const HasPtr &obj)
	:ptr(obj.ptr), val(obj.val)
{
	++obj.ptr->count;

	cout << "copy constructor" << endl;
}
这里主要增加使用计数,因为复制后就多了一个对象占有这个指针了

赋值操作符:

HasPtr& HasPtr::operator=(const HasPtr &obj)
{
	cout << "operator = " << endl;
#if   0 //注意这里的u_ptr可能指向两个不同的指针对象
	++obj.ptr->count;
	if (--ptr->count == 0)
	{
		delete ptr;
	}
#else
	--ptr->count;
	++obj.ptr->count;
	if (ptr->count == 0)
	{
		delete ptr;
	}
#endif
	ptr = obj.ptr;
	val = obj.val;
	return *this;
}
这里赋值默认要先去掉之前对象的指针使用计数,然后再增加形参对象的计数,这里要判断一下,是否当前对象对指针的使用计数为0,如果为0就要释放那个指针,否则就会导致一块内存永远得不到释放。两种方式是一致的,主要考虑赋值自己的问题,理解方式不一样,不再深究。

析构函数:

HasPtr::~HasPtr(void)
{
	if (--ptr->count == 0)
	{
		delete ptr;
	}
	cout << "destructor" << endl;
}
这里当指针计数为0的时候,肯定要释放他,为什么要减1,主要是由于对象的生命期已经结束了,没有结束就不会进入析构函数,所以指针计数肯定要-1 。但是在C++primer里面对智能指针类里面的析构函数,再次释放是不正确的,看上面智能指针类里面的标注,HasPtr里面申请的资源必须由类自己来释放,而智能指针类u_ptr里面传入的p指向的对象其实就是HasPtr构造函数传入的形参p指向的对象,他是不是动态分配的,我们不知道,而且这个由类的使用者决定,所以智能指针不能使用delete ip,否则会两次释放同一块内存,会出错的!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值