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;
}
即使要将一个对象赋值给它本身,赋值操作符也必须保证正确。
本例中,即使左右操作数相同,操作也是安全的,因此,不必显示检查自身赋值。