c++ primer智能指针实现

class U_Ptr{
	friend class HasPtr;
	int *ip;
	int use;
	U_Ptr(int *p): ip(p),use(1){
		cout<<"U_Ptr() called \n";
	}
	~U_Ptr(){
		delete ip;
		cout<<"~U_Ptr() called \n";
	}
};

上面是计数类,为了HasPtr类访问,U_Ptr类把HasPtr 类设为友元。两个类的关系如下图所示:

 

里面有个变量use和指针ip: use记录了*ip对象被多少个HasPtr对象所指。假设现在又两个HasPtr对象p1,p2指向了U_Ptr,那么现在我delete  p1,use变量将自减1,  U_Ptr不会析构,那么U_Ptr指向的对象也不会析构,那么p2仍然指向了原来的对象,而不会变成一个悬空指针。当delete p2的时候,use变量将自减1,为0。此时,U_Ptr对象进行析构,那么U_Ptr指向的对象也进行析构,保证不会内存泄露。  

大多数C++类用三种方法之一管理指针成员

    (1)不管指针成员。复制时只复制指针,不复制指针指向的对象。当其中一个指针把其指向的对象的空间释放后,其它指针都成了悬浮指针。这是一种极端

   (2)当复制的时候,即复制指针,也复制指针指向的对象。这样可能造成空间的浪费。因为指针指向的对象的复制不一定是必要的。

   (3)第三种就是一种折中的方式。利用一个辅助类来管理指针的复制。原来的类中有一个指针指向辅助类,辅助类的数据成员是一个计数器和一个指针(指向原来的)(此为本次智能指针实现方式)。

     其实,智能指针的引用计数类似于java的垃圾回收机制:java的垃圾的判定很简答,如果一个对象没有引用所指,那么该对象为垃圾。系统就可以回收了

HasPtr类的实现。

class HasPtr{
private:
	U_Ptr  *ptr;
	int val;
public:
	HasPtr(int *p,int i):ptr(new U_Ptr(p)),val(i){
		cout << "HasPtr constructor called ! " << "use = " << ptr->use << endl;
	}
	HasPtr(const HasPtr& orig):ptr(orig.ptr),val(orig.val){
		ptr->use++;
		cout << "HasPtr copy constructor called ! " << "use = " << ptr->use << endl;
	}
	HasPtr & operator=(const HasPtr&);
	
	~HasPtr() {
		cout << "HasPtr ~HasPtr() called ! " << "use = " << ptr->use << endl;
		if (--ptr->use == 0)
			delete ptr;	
	}
	int *get_ptr()const
	{
		return ptr->ip;
	}
	int get_int ()const
	{
		return val;
	}
	void set_ptr(int *p)
	{
		ptr->ip=p;
	}
	void set_int(int i)
	{
		val=i;
	}
	int get_ptr_val()const 
	{
		return *ptr->ip;
	}
	void set_ptr_val(int i)
	{
		*ptr->ip=i;
	}
};


main函数调用情况

#include "smart_point.h"
#include "another.h"

#include <iostream>
using namespace std;

int main()
{
	int *pi = new int(0);
	HasPtr *p1 = new HasPtr(pi, 100);
	HasPtr *p2 = new HasPtr (*p1);
	cout<<p1->get_int()<<" "<<p1->get_ptr()<<" "<<p1->get_ptr_val()<<endl;
	cout<<p2->get_int()<<" "<<p2->get_ptr()<<" "<<p2->get_ptr_val()<<endl;
	HasPtr obj=*p1;
	cout<<obj.get_int()<<" "<<obj.get_ptr()<<" "<<obj.get_ptr_val()<<endl;
	delete p1;
	delete p2;
//	HasPtr a(pi,20);
	return 0;
}


测试时候使用了HasPtr a(pi,20); 这使得程序运行出现

_BLOCK_TYPE_IS_VALID(pHead->nBlockUse 错误

后来百度到原因是:

问题产生原因:

      1.内存泄漏;所以当程序退出时,系统会收回分配的内存,于是调析构函数,由于内存已被错误地释放,于是就会出现“Debug Assertion Failed”的错误。

      2.这个assert说明什么问题呢?说明有一块内存在被释放的时候,它的头部里面的信息已经被改掉了,和预期的不一样。内存分配的程序往往在被分配出的内存块头部放上一些校验信息。这个信息内存的用户是不知道也不应该修改的。这样,在内存被释放的时候,内存分配程序就可以验对这个头部信息是否被改过了。若被改过,就说明发生了内存corruption. 

     这种corruption有两种可能性:

     1)有人在内存越界写东西;

     2)这块内存已经被释放了,又被重复释放了一次。(在第一次被释放中,是内存分配程序改掉了头部信息)。

     3.pHead_>nBlockUse就可能是空指针,或它指向的东西已经不存在了。 

     解决办法:

     一般来说,这个问题产生的原因大部分是第二种情况,而corruption也最可能是第二种情况,即重复释放内存。对于D3DX程序来说,如果是用的D3DX的API接口创建的指针,则在Release之后不需要也不能再调用delete来释放内存,因为Release本来就已经释放了内存,再次调用delete就会造成重复释放内存,也就是本文所描述的那个问题。那么对应的解决办法就是删掉Release后面所对应的delete语句就OK了。(注意,D3DX的API创建的指针只需要Release就OK,如果是自己创建的新的class,那么最好加上delete语句来释放内存。)

HasPtr a(pi,20); 是栈上分配内存,当程序结束是调用析构函数,此时内存已经释放,析构函数释放内存出现上述错误。, 析构函数delete只能删除堆上内存, 栈上的内存, 是不需要手动来释放的, 因此, 程序会说内存块的类型不对.


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值