例子:
对于邮件处理应用程序,Message类和Folder类分别表示电子邮件消息和消息出现的目录,一个给定的消息可以出现在多个目录中。
Message和Folder类设计如图:
对于每个Message,我们并不是在每个Folder中都存放一个副本,而是使每个Message保存一个指针集set,set中的指针指向该Message所在的Folder。每个Folder中也有一个指针集,其中的指针指向它所包含的Message。
1.所以对于Message类,它有私有的成员:
set<Folder*> folders,用于保存指向它所在Folder的指针。
string contents,用于保存消息内容。
对于Folder类,它有私有成员:
Set<Message*>messages,用于保存指向它所包含的Message的指针。
2.创建新的Message对象时,将指定消息的内容但不指定Folder。调用save操作将Message放入一个Folder。
在创建新的Message对象时,需要调用接受单个string形参的构造函数如下:
Message(const string str="") : contents(str){}
构造函数将消息的副本保存在contents中,并将set<Folder*> folders隐式初始化为空集,因为这个构造函数提供一个默认实参(空串),所以它也可以作为默认构造函数。
创建新的Folder对象时,调用的默认构造函数如下:
Folder(){}
3.当复制一个Message对象时,将复制原始消息的内容和Folder指针集,还必须给指向源Message的每个Folder增加一个指向该Message的指针,这个过程用put_Msg_in_Folders(const set<Folder*>&)来实现,这个成员函数是私有的,因为客户程序不需要调用到它。
Message::Message(Message &m): contents(m.contents), folders(m.folders)
{
put_Msg_in_Folders(folders);
}
void Message::put_Msg_in_Folders(constset<Folder*>&rhs)
{
for (set<Folder*>::const_iterator beg=rhs.begin();
beg!=rhs.end(); ++beg)
(*beg)->addMsg(this);
}
其中addMsg是Folder类的public成员函数,用于将Message对象的指针加入到Folder类对象的messages成员变量中。
void Folder::addMsg(Message*msg)
{
messages.insert(msg);
}
当复制一个Folder对象时,不仅要复制成员messages,还要在源Folder所指向的每一个Message对象的folders成员中增加自己的this指针。这个功能使用void put_Foldr_in_Msgs(constset<Message*>&)这个私有的函数来完成。
Folder::Folder(const Folder &f) : messages(f.messages)
{
put_Foldr_in_Msgs(messages);
}
void Folder::put_Foldr_in_Msgs(constset<Message*>&msg)
{
for (set<Message*>::const_iterator beg=msg.begin();
beg!=msg.end(); ++beg)
(*beg)->addFldr(this);
}
其中的addFldr是Message的成员函数,用于将Folder对象的指针加到Message对象的folders成员变量中去。
void Message::addFldr(Folder*f)
{
folders.insert(f);
}
4.将一个Message对象赋值给另一个,类似于复制一个Message:复制之后,contents和folders集合是相同的。首先从左边Message在复制之前所处的Folder中删除该Message。原来的Message去掉后,再将右边操作数的内容和folders集复制到左边,还必须在这个folders集中的每个Folder对象中增加一个指向左边的Message的指针。这就需要重载赋值操作符了。
Message& Message::operator=(const Message &msg)
{
if (&msg!=this) {
remove_Msg_from_Folders();
contents= msg.contents;
folders= msg.folders;
put_Msg_in_Folders(msg.folders);
}
return *this;
}
其中emove_Msg_from_Folders()用于将左边Message对象从所有包含它的Folder中删除。用if做判断的原因是若不判断,如果出现左右操作数相同的情况。则folders = msg.folders这一步得到的folders就不正确了。
void Message::remove_Msg_from_Folders()
{
for (set<Folder*>::const_iterator beg=folders.begin();
beg!=folders.end();++beg)
(*beg)->removeMsg(this);
}
其中void removeMsg(Message*)用于删除Folder对象messages成员中的Message对象。
void Folder::removeMsg(Message*msg)
{
messages.erase(msg);
}
同理,将Folder对象赋值给另一个是一样的操作。
Folder& Folder::operator=(const Folder &f)
{
if (&f!=this) {
remove_Fldr_from_Msgs();
messages= f.messages;
put_Fldr_in_Msgs(f.messages);
}
return *this;
}
其中remove_Fldr_from_Msgs()用于将左边Folder对象从所有包含它的Message中删除。
void Folder::remove_Fldr_from_Msgs()
{
for (set<Message*>::const_iterator beg=messages.begin();
beg!=messages.end();++beg)
(*beg)->removeFldr(this);
}
其中void removeFldr(Folder*)用于删除Message对象folders成员中的Folder对象。
void Message::removeFldr(Folder*f)
{
folders.erase(f);
}
5.Message析构函数:
~Message()
{
remove_Msg_from_Folders();
}
有了remove_Msg_from_Folders()函数,编写析构函数将非常简单。我们调用remove_Msg_from_Folders()函数清除folders,系统自动调用string析构函数释放contents,自动调用set析构函数清除用于保存folders成员的内存清除用于保存folders成员的内存。因此,Message析构函数唯一要做的是调用remove_Msg_from_Folders()。
对于Folder类是同理的。
6.对于一些保存和删除操作:在创建新Message对象时指定消息内容但不指定Folder。调用save将Message放入一个Folder,调用remove将其从一个Folder中移除。
void Message::save(Folder&f)
{
addFldr(&f);
f.addMsg(this);
}
void Message::remove(Folder&f)
{
removeFldr(&f);
f.removeMsg(this);
}
void Folder::save(Message&msg)
{
addMsg(&msg);
msg.addFldr(this);
}
void Folder::remove(Message&msg)
{
removeMsg(&msg);
msg.removeFldr(this);
}
其实,其中额各种关系还是挺复杂的~