复制控制 - 2【C++ Primer 学习笔记 - 第十三章】

class Folder
{
public:
	void addMsg(const Message*);
	void remMsg(const Message*);
};
class Message
{
public:
	// folders 成员会隐式的,被初始化为空集。
	Message(const string &str = ""):contents(str){}
	Message(const Message&);
	Message& operator=(const Message&);
	~Message();

	void save(Folder&);
	void remove(Folder&);
private:
	string contents;	// 保存实际消息内容
	
	// Folder 类型包含多个指针,都是指向 Message 类型的
	// Folder 类型,类似于 set<Message*>
	// 相应的,folders 成员,保存了许多 Folder 指针
	// 这些指针所指向的 Folder 对象,都包含当前这个 Message 的指针
	set<Folder*> folders;	
	void put_Msg_in_Folders(const set<Folder*>&);
	void remove_Msg_from_Folders();
};


// 复制 Message 的时候,
// 必须将,新的 Message,添加到保存原 Message 的每个 Folder 中
Message::Message(const Message &m):contents(m.contents),
	folders(m.folders)
{
	put_Msg_in_Folders(folders);
}
void Message::put_Msg_in_Folders(const set<Folder*>&rhs)
{
	for(set<Folder*>::const_iterator beg = rhs.begin();
		beg != rhs.end(); ++beg)
	{
		// (*beg) :对 beg 解引用,得到 Folder 的指针
		// 对 Folder 的指针,使用 -> 调用 addMsg 成员函数
		(*beg)->addMsg(this);
	}
}
Message& Message::operator=(const Message &rhs)
{
	// 对 rhs 取地址,与 this 指针比较。
	// 如果不同,执行以下操作
	if(&rhs != this)
	{
		remove_Msg_from_Folders();
		contents = rhs.contents;
		folders = rhs.folders;
		put_Msg_in_Folders(rhs.folders);
	}
	return *this;
}

void Message::remove_Msg_from_Folders()
{
	for (set<Folder*>::const_iterator beg = folders.begin();
		beg != folders.end(); ++beg)
	{
		(*beg)->remMsg(this);
	}
}

Message::~Message()
{
	remove_Msg_from_Folders();
}



使用标准库,能够大大减少 C++程序中,对指针的需要。
使用指针,需要特别注意,复制控制。
假设:
类 A,具有一个指针成员 int *imem。
然后,创建了 类A 的两个对象 a1、a2,
并且 a1、a2 中的imem,都指向同一个 int 对象 i
那么,如果,使用 a1 改变了 i 的值,甚至删除了 i
但是,a2 有可能还认为 i 是存在的。出现悬垂指针的问题
为避免上述情况,要制定相应的复制控制策略。


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_int(int i){ val = i;}

	int get_ptr_val() const{ return *ptr;}
	int set_ptr_val(int val) const{ *ptr = val;}
private:
	int *ptr;
	int val;
};

/* ... */

int obj = 0;
HasPtr ptr1(&obj, 42);
// 使用的是默认合成的复制构造函数,有很多缺点
HasPtr ptr2(ptr1);

ptr1.set_int(0);
ptr1.get_int();	// 0
ptr2.get_int();	// 42
	
ptr1.set_ptr_val(100);
ptr1.get_ptr_val();	// 100
ptr2.get_ptr_val();	// 100

int *ip = new int(88);
HasPtr ptr(ip, 7);
delete ip;
ptr.set_ptr_val(5);	// 报错,悬垂指针


智能指针类
需要,使用计数,或称,引用计数。

int obj;
// 假如计数器,保存在 HasPtr 对象中,
// 那么创建 p3 时,可以先对 p1 中的计算 +1,然后复制到 p3
// 但是,此时,p2 中的计数器,未能更新。
// 因此,这种做法,不可取。
HasPtr p1(&obj, 40);
HasPtr p2(p1);
HasPtr p3(p1);

解决上述问题,的经典策略之一:使用 计数类
class U_Ptr
{
	// 以下成员均为 private
	// 我们并不希望,普通用户使用 U_Ptr 类,
	// 只允许友元 HasPtr 访问 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_int(int i){ val = i;}

	int get_ptr_val() const{ return *ptr->ip;}
	int set_ptr_val(int val) const{ *ptr->ip = val;}
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;
}

定义值型类

处理指针成员的另一方法:给指针成员,提供值语义(value semantics)。
要使指针成员表现的像一个值,复制 HasPtr 对象的时候,必须复制指针所指向的对象。

class HasPtr
{
public:
	HasPtr(const int &p, int i):ptr(new int(p)), val(i){}
	HasPtr(const HasPtr &orig):ptr(new int (*orig.ptr)), val(orig.val){}
	HasPtr& operator=(const HasPtr&);
	~HasPtr()
	{
		delete ptr;
	}

	int *get_ptr() const{ return ptr;}
	int get_int() const{ return val;}

	void set_ptr(int *p){ ptr = p;}
	void set_int(int i){ val = i;}

	int get_ptr_val() const{ return *ptr;}
	int set_ptr_val(int val) const{ *ptr = val;}
private:
	int *ptr;
	int val;
};
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{	
	*ptr = *rhs.ptr;
	val = rhs.val;
	return *this;
}
即使要将一个对象赋值给它本身,赋值操作符也必须保证正确。
本例中,即使左右操作数相同,操作也是安全的,因此,不必显示检查自身赋值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值