管理指针成员
此话题是针对还有指针成员的类提起的,对于这样的类在管理时理应特别小心。
原因如下:
① 注意指针成员的初始化问题
② 注意指针成员的复制问题
③ 注意指针成员的释放问题
④ 注意指针成员的垂悬问题
⑤ 注意指针成员的赋值问题
首先我们来看一个含有指针成员的类:
class HasPtr
{
public:
HasPtr(int *p,int i):ptr(p),val(i){}
int *get_ptr()const //返回指针成员
{
return ptr;
}
int get_int()const
{
return val;
}
void set_ptr(int *p) //设置指针
{
ptr = p;
}
void set_val(int i)
{
val = i;
}
int get_ptr_val()const //返回指针成员的值
{
return *ptr;
}
void set_ptr_val()const //设置指针成员的值
{
*ptr = val;
}
private:
int *ptr;
int val;
};
我们对指针成员的管理有三种方法:
① 指针成员采取常规的行为
这类管理方法产生的类含有指针的所有缺陷,他不设置特殊的复制控制。即程序员无需自己定义复制构造函数和赋值函数。
下面我们看一下这个方法的缺陷。
(1)int obj = 0;
HasPtr ptr1(&obj,42);
HasPtr ptr2(ptr1);
此定义使对象 ptr1,ptr2中的指针成员均指向了obj.使两个对象失去了独立性。
(2)同时这样当我们修改其中一个对象的值时,同时也改变了另一个对象的值。
(3)对于这样的代码
int *ip = new int(42);
HasPtr ptr(ip,10);
delete ip;
ptr.set_ptr_val(0);
然而当我们删除保存int 对象的ip指针时,ptr就不指向有效的对象了。但是我们进行运行输出后,结果却是正确的,这是怎么回事呢?原本这里我也不理解,多亏一个朋友帮我解决了这个问题。谢谢他,这里之所以结果还是正确的,是因为我们申请空间属于堆内存,虽然我们这里释放了指针ip,但由于以后我们并没有申请空间,即原来ptr指向的那块内存并没有被占,所以他还保存着原来的值,所以我们这里修改成功了。若我们在输出前再来个动态分配,结果就不对了。。
② 使用智能指针
所谓智能指针就是用来负责删除共享对象的指针,他可以判断当前是否还有指针指向该共享对象,若当前已是最后一个指向共享对象的指针,就删除共享对象。由此可知我们需要计数,统计指向共享对像的指针个数。我们每次定义一个类对象时,我们给他的指针成员复制的共享对象可能不同,故我们需要对每一个类对象进行计数。用来统计指向该共享对象的指针个数。这样在定义类的时候就可以在构造函数中将计数置1.在复制构造函数中将计数加1.在赋值函数中对右操作数的计数加1,对左操作数的计数减1.最后在调用析构函数的时候计数减1 然后判断计数是否为0,若为0,则删除共享对象。但是我们不能直接把计数放在对象中。否则会出现下面情况:
int obj;
HasPtr p1(&obj,42);
HasPtr p2(p1);
HasPtr p3(p1);
这段代码中我们让对象p2,p3指向了同一个共享对象,按我们上面的方法,我们分别在对象p2,p3中对指向该共享对象的指针做了计数,然而我们统计的仅仅是分别有p2,p3产生的指针数目。即若p2(或p3)中的数目为0时,按我们的设计即可删除共享对象p1,不管此时p3(或p2)中的数目是否为0.故这样就产生了垂悬指针的情况。况且我们无法对这样的情况对计数统一起来。故我们用另外一种方法,专设一个计数类。
class U_Ptr{
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p):ip(p),use(1){}
~U_Ptr(){delete ip;}
};
计数类的使用:
class HasPtr{
public:
HasPtr(int *p,int i):ptr(new U_Ptr(p)),val(i){}
HasPtr(const HasPtr,&orig):ptr(orig.ptr),val(orig.val){++ptr->use;}
HasPtr& operator=(const HasPtr&);
~HasPtr(){if (--ptr->use == 0)delete ptr;}
private:
U_Ptr *ptr;
int val;
};
我们在定义一个对象时,对象指向的不在是共享对象,而是一个U_Ptr对象。U_Ptr对象可以由共享对象的指针进行初始化,而后我们在U_Ptr对象中进行计数,这样就预防了上述问题的发生。即,由某个U_Ptr对象初始化的指针数目为0时即可删除该U_Ptr对象。这样不会对共享对象产生任何影响。。
这里类里对于计数的设计和上面一样。
不过这样设计,仍存在一个问题,那就是改变一个对象,将会改变共享对象,所有共享该对象的对象值均会被改变。
③ 定义值类型
这种方法就是我们平时最常用的方法了,即我们为我们所定义的指针重新复制一个副本。这样他们之间就各自独立,互不影响了,同时就解决了以上所有的问题。
此总结 参考《C++ Primer》