拷贝控制
--消息处理演示样例
说明:
有些类为了做一些工作须要对复制进行控制。
为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序。Message类和 Folder类分别表示电子邮件(或其它)消息和消息所出现的文件夹,一个给定消息能够出如今多个文件夹中。
Message上有 save和 remove操作,用于在指定Folder中保存或删除该消息。
数据结构:
对每一个Message,我们并非在每一个Folder中都存放一个副本,而是使每一个Message保存一个指针集(set),set中的指针指向该Message所在的Folder。
每一个Folder也保存着一些指针,指向它所包括的Message。
数据结构如图所看到的。
操作:
创建新的Message时,将指定消息的内容但不指定Folder。调用save将 Message放入一个Folder。
复制一个Message对象时,将复制原始消息的内容和Folder指针集,还必须给指向源 Message的每一个Folder添加一个指向该Message的指针。
将一个Message对象赋值给还有一个,相似于复制一个Message:赋值之后,内容和 Folder集将是同样的。
首先从左边Message在赋值之前所处的Folder中删除该Message。原来的Message去掉之后,再将右边操作数的内容和Folders集拷贝到左边,还必须在这个Folder集中的每一个Folders中添加一个指向左边Message的指针。
撤销一个Message对象时,必须更新指向该Message的每一个 Folder。一旦去掉了 Message,指向该Message的指针将失效,所以必须从该Message的Folder指针集的每一个Folder中删除这个指针。
能够看到,析构函数和赋值操作符分担了从保存给定Message的 Folder列表中删除消息的工作。相似地,复制构造函数和赋值操作符分担将一个Message加到给定Folder列表的工作。我们将定义一对private有用函数完毕这些任务。
实现:
1、Message类
class Message
{
public:
Message(const std::string &str = ""):contents(str){}; Message(const Message &);
Message &operator=(const Message &);
~Message(); void save(Folder &);
void remove(Folder &); private:
std::string contents;
std::set<Folder *> folders; void put_Msg_in_Folders(const std::set<Folder *> &);
void remove_Msg_from_Folders();
};
put_Msg_in_Folders函数将自身Message的一个副本加入到指向给定Message的各Folder中,这个函数运行完后,形參指向的每一个Folder也将指向这个Message。复制构造函数和赋值操作符都将使用这个函数。
remove_Msg_from_Folders函数用于赋值操作符和析构函数,它从folders成员的每一个Folder中删除指向这个Message的指针。
2、Message类的复制控制
复制Message时,必须将新创建的Message加入到保存原Message的每 个 Folder中。
这个工作超出了合成构造函数的能力范围,所以我们必须定义自己的复制构造函数:
Message::Message(const Message &m):
contents(m.contents),folders(m.folders)
{
put_Msg_in_Folders(folders);
}
复制构造函数将用旧对象成员的副本初始化新对象的数据成员。除了这些初始化之外(合成复制构造函数能够完毕这些初始化),还必须用folders进行迭代,将这个新的Message加到那个集的每一个Folder中。复制构造函数使用put_Msg_in_Folder函数完毕这个工作。
编写自己的复制构造函数时,必须显式复制须要复制的随意成员。显式定义的复制构造函数不会进行不论什么自己主动复制。
像其它不论什么构造函数一样,假设没有初始化某个类成员,则那个成员用该成员的默认构造函数初始化。复制构造函数中的默认初始化不会使用成员的复制构造函数。
3、put_Msg_in_Folder成员
put_Msg_in_Folders通过形參 rhs的成员 folders中的指针进行迭代。
这些指针表示指向rhs的每一个Folder,须要将指向这个Message的指针加到每一个Folder。
函数通过rhs.folders进行循环,调用命名为addMsg的 Folder成员来完毕这个工作,addMsg函数将指向该Message的指针加到Folder中。
void Message::put_Msg_in_Folders(const set<Folder *> &rhs)
{
for (set<Folder *>::const_iterator beg = rhs.begin();
beg != rhs.end(); ++beg)
{
/*
*(*beg)解除迭代器引用。 解除迭代器引用将获得一个指向 Folder 的指针
*然后表达式对 Folder 指针应用箭头操作符以运行addMsg 操作
*将 this 传给 addMsg,该指针指向我们想要加入到 Folder 中的Message
*/
(*beg) -> addMsg(this);
}
}
4、Message赋值操作符
赋值比复制构造函数更复杂。像复制构造函数一样,赋值必须对contents赋值并更新folders使之与右操作数的folders相匹配。它还必须将该Message加到指向rhs的每一个 Folder中,能够使用put_Msg_in_Folders函数完毕赋值的这一部分工作【可是须要注意的是函数的实參此时由folders换成了rhs.folders了】。
在从rhs复制之前,必须首先从当前指向该Message的每一个Folder中删除它。我们须要通过folders进行迭代,从folders的每一个Folder中删除指向该Message的指针。命名为remove_Msg_from_Folders的函数将完毕这项工作。
对于完毕实际工作的remove_Msg_from_Folders和put_Msg_in_Folders,赋值操作符本身相当简单:
Message &Message::operator=(const Message &rhs)
{
if (&rhs != this)
{
remove_Msg_from_Folders();
contents = rhs.contents;
folders = rhs.folders;
put_Msg_in_Folders(rhs.folders);
} return *this;
}
假定操作数是不同对象,调用remove_Msg_from_Folders从 folders成员的每一个Folder中删除该Message。
一旦这项工作完毕,必须将右操作数的contents和 folders成员赋值给这个对象。
最后,调用put_Msg_in_Folders将指向这个Message的指针 加入至指向rhs的每一个 Folder中。
了解了remove_Msg_from_Folders的工作之后,我们来看看为什么赋值操作符首先要检查对象是否不同。赋值时需删除左操作数,并在撤销左操作数的成员之后,将右操作数的成员赋值给左操作数的对应成员。假设对象是同样的,则撤销左操作数的成员也将撤销右操作数的成员!
即使对象赋值给自己,赋值操作符的正确工作也很重要。保证这个行为的通用方法是显式检查对自身的赋值。
5、remove_Msg_from_Folders成员
void Message::remove_Msg_from_Folders()
{
for (set<Folder *>::iterator beg = folders.begin();
beg != folders.end(); ++beg)
{
(*beg) -> remMsg(this);
}
}
6、Message析构函数
Message::~Message()
{
remove_Msg_from_Folders();
}
有了remove_Msg_from_Folders函数,编写析构函数将很easy。
我们调用remove_Msg_from_Folders函数清除folders,系统自己主动调用string析构函数释放contents,自己主动调用set析构函数清除用于保存folders成员的内存,因此,Message析构函数唯一要做的是调用remove_Msg_from_Folders。
【最佳实践:】
赋值操作符通常须要做复制构造函数函数和析构函数也要完毕的工作。在这种情况下。通用工作应该放在private有用函数中。
//P419 习题13.17
//under the private labor
void addFldr(Folder *f)
{
folders.insert(f);
}
void remFldr(Folder *f)
{
folders.erase(f);
}
//习题13.19
void Message::save(Folder &f)
{
folders.insert(&f);
f.addMsg(this);
} void Message::remove(Folder &f)
{
folders.erase(&f);
f.remMsg(this);
}
//拓展:完毕完整的类Folder和Message,完毕习题13.16~13.19内容
//in Folder.h
#ifndef FOLDER_H_INCLUDED
#define FOLDER_H_INCLUDED #include <set>
#include <string> class Message;
class Folder
{
public:
Folder() {}
~Folder(); void addMsg(Message *);
void remMsg(Message *); private:
std::set<Message *> messages;
void remove_Fldr_form_Messages();
}; class Message
{
public:
Message(const std::string &str = ""):contents(str) {}; Message(const Message &);
Message &operator=(const Message &);
~Message(); void save(Folder &);
void remove(Folder &); void addFldr(Folder *);
void remFldr(Folder *); private:
std::string contents;
std::set<Folder *> folders; void put_Msg_in_Folders(const std::set<Folder *> &);
void remove_Msg_from_Folders();
}; #endif // FOLDER_H_INCLUDED
//in Folder.cpp
#include "Folder.h"
#include <set>
using namespace std; Folder::~Folder()
{
remove_Fldr_form_Messages();
} void Folder::addMsg(Message *rhs)
{
messages.insert(rhs);
}
void Folder::remMsg(Message *rhs)
{
messages.erase(rhs);
} void Folder::remove_Fldr_form_Messages()
{
for (std::set<Message *>::const_iterator beg = messages.begin();
beg != messages.end(); ++beg)
{
(*beg) -> remFldr(this);
}
} Message::Message(const Message &m):contents(m.contents),folders(m.folders)
{
put_Msg_in_Folders(folders);
}
Message &Message::operator=(const Message &rhs)
{
if (&rhs != this)
{
remove_Msg_from_Folders();
contents = rhs.contents;
folders = rhs.folders;
put_Msg_in_Folders(rhs.folders);
}
return *this;
}
Message::~Message()
{
remove_Msg_from_Folders();
} void Message::put_Msg_in_Folders(const set<Folder *> &rhs)
{
for (set<Folder *>::const_iterator beg = rhs.begin();
beg != rhs.end(); ++beg)
{
(*beg) -> addMsg(this);
}
}
void Message::remove_Msg_from_Folders()
{
for (set<Folder *>::iterator beg = folders.begin();
beg != folders.end(); ++beg)
{
(*beg) -> remMsg(this);
}
} void Message::save(Folder &f)
{
folders.insert(&f); //更新Message所属的文件夹
f.addMsg(this); //更新文件夹
}
void Message::remove(Folder &f)
{
folders.erase(&f); //更新Message正确的文件夹
f.remMsg(this); //更新文件夹
} void Message::addFldr(Folder *f)
{
folders.insert(f);
}
void Message::remFldr(Folder *f)
{
folders.erase(f);
}