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 错误
后来百度到原因是:
问题产生原因:
HasPtr a(pi,20); 是栈上分配内存,当程序结束是调用析构函数,此时内存已经释放,析构函数释放内存出现上述错误。, 析构函数delete只能删除堆上内存, 栈上的内存, 是不需要手动来释放的, 因此, 程序会说内存块的类型不对.