C++中类指针成员的处理问题和智能指针

这是在《C++ Primer》第四版P419面,13.5.管理指针成员讲到的,这里概括下,并用例子说明问题。
1、大家都知道,对于类中包含指针成员,则一般由编译器合成的复制构造函数,赋值操作符,以及析构函数无法完成要求,这是因为“浅复制”只复制指针的值,而不是复制指针所指对象的值。那么这个时候就该选择进行“深复制”了!

2、大多数 C++ 类采用以下三种方法之一管理指针成员
    1.指针成员采取常规指针型行为。这样的类具有指针的所有缺陷但无需特殊的复制控制。
    2.类可以实现所谓的“智能指针”行为。指针所指向的对象是共享的,但类能够防止悬垂指针。
    3.类采取值型行为。指针所指向的对象是唯一的,由每个类对象独立管理。

3、现在具体谈下第二个方法:实现“智能指针”(smart point)。
定义智能指针的通用技术是采用一个使用计数。智能指针类将一个计数器与类指向的对象相关联。使用计数跟踪该类有多少个对象共享同一指针。使用计数为 0 时,删除对象。使用计数有时也称为引用计数
以下是智能指针类的特点:(重点
每次创建类的新对象时,初始化指针并将使用计数置为 1。当对象作为另一对象的副本而创建时,复制构造函数复制指针并增加与之相应的使用计数的值。对一个对象进行赋值时,赋值操作符减少左操作数所指对象的使用计数的值(如果使用计数减至 0,则删除对象),并增加右操作数所指对象的使用计数的值。最后,调用析构函数时,析构函数减少使用计数的值,如果计数减至 0,则删除基础对象。
4、记数类
实现使用计数有两种经典策略,在这里给出其中一种方法,需要定义一个单独的具体类用以封闭使用计数和相关指针。

class U_Ptr {
	friend class HasPtr;
	int *ip;
	size_t use;
	U_Ptr(int *p): ip(p), use(1) {}
	~U_Ptr() { delete ip; }
};

以下是我写的例子:

// Author: Tanky Woo
// Blog:    www.WuTianQi.com
#include 
using namespace std;
 
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; }
 
	int *get_ptr() const { return ptr->ip; }
	int get_int() const { return val; }
	void set_ptr(int *p) { ptr->ip = p; }
	void set_val(int i) { val = i; }
	int get_ptr_val() const { return *ptr->ip; }
	void set_ptr_val(int i) const { *ptr->ip = i; }
private:
	U_Ptr *ptr;
	int val;
};
 
HasPtr& HasPtr::operator =(const HasPtr & rhs)
{
	++rhs.ptr->use;
	if(--ptr->use == 0)
		delete ptr;
	ptr = rhs.ptr;
	val = rhs.val;
	return *this;
}
 
int main()
{
	freopen("output.txt", "w", stdout);
	int *m = new int;
	*m = 10;
	HasPtr B(m, 5);
	cout << "B,m:" << B.get_ptr() << endl << endl;
	HasPtr A = B;
	cout << "B,m:" << B.get_ptr() << endl;
	cout << "A,m:" << A.get_ptr() << endl << endl;
	B.~HasPtr();
	cout << "B,m:" << B.get_ptr() << endl;
	cout << "A,m:" << A.get_ptr() << endl << endl;
 
	A.~HasPtr();
	cout << "B,m:" << B.get_ptr() << endl;
	cout << "A,m:" << A.get_ptr() << endl << endl;
}

结果:(VS2008, Tanky Woo的电脑中测试结果)
B,m:00395B38 (第一组)

B,m:00395B38 (第二组)
A,m:00395B38

B,m:00395B38 (第三组)
A,m:00395B38

B,m:DDDDDDDD (第四组)
A,m:DDDDDDDD

注意结果,千三阻结果一样,都和第一组m的地址一样。第四组就和前三组不一样了。
在析构B后,并没有使指针撤销,而是当全部指向m所指向对象的记数为0时,才撤销的。

对于第三个管理指针方法定义值型类,这里顺便说下:
处理指针成员的另一个完全不同的方法,是给指针成员提供值语义。具有值语义的类所定义的对象,其行为很像算术类型的对象:复制值型对象时,会得到一个不同的新副本。对副本所做的改变不会反映在原有对象上,反之亦然。string 类是值型类的一个例子。

发布者

Tanky Woo

Tanky Woo,[个人主页:https://tankywoo.com] / [新博客:https://blog.tankywoo.com]

《C++中类指针成员的处理问题和智能指针》有68个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注