13.33:为什么Message的成员save和remove的参数时一个Folder &?为什么我们不将参数定义为Folder或是const Folder &?
成员内部调用的函数需要改变Folder中的元素,Folder或是const Folder &都无法做到。
13.34:编写本节所描述的Message。
class Folder;
class Message {
friend void swap(Message &, Message &);
string contents;
set<Folder*> folders;
void add_to_Folders(const Message &);
void remove_form_Folders();
public:
explicit Message(const string &str = "") :contents(str) {}
Message(const Message &);
~Message();
Message & operator = (const Message &);
void save(Folder &);
void remove(Folder &);
};
void swap(Message &, Message &);//声明
//容器set内部会构造指针的副本
Message::Message(const Message &msg) :contents(msg.contents), folders(msg.folders) {
add_to_Folders(msg);
}
Message::~Message() {
remove_form_Folders();
}
Message & Message::operator = (const Message &msg) {
remove_form_Folders();
contents = msg.contents;
folders = msg.folders;//容器set内部会构造指针的副本
add_to_Folders(msg);//将本消息添加到指向msg的所有Folder中
return *this;
}
void Message::save(Folder &fd) {
folders.insert(&fd);
fd.addMsg(this);
}
void Message::remove(Folder &fd) {
folders.erase(&fd);
fd.remMsg(this);
}
void Message::add_to_Folders(const Message &msg) {
for (auto f : msg.folders)
f->addMsg(this);
}
void Message::remove_form_Folders() {
for (auto f : folders)
f->remMsg(this);
}
void swap(Message &lhs, Message &rhs) {
using std::swap;
for (auto f : lhs.folders)
f->remMsg(&lhs);
for (auto f : rhs.folders)
f->remMsg(&rhs);
swap(lhs.folders, rhs.folders);
swap(lhs.contents, rhs.contents);
for (auto f : lhs.folders)
f->addMsg(&lhs);
for (auto f : rhs.folders)
f->addMsg(&rhs);
}
13.35:如果Message使用合成版本的拷贝控制成员,将会发生什么?
当拷贝一个Message时,副本的指针将不会出现在任何一个Folder中(尽管它包含了其所在Folder的指针set)。更严重的,当某Message析构后,其所在Folder中任然保留了该Message的指针,该指针是无效的!
13.36:设计并实现对应的Folder类。此类应该保存一个指向Folder中包含的Message的set。
class Folder {
set<Message*> messages;
public:
void addMsg(Message *const pMsg) { messages.insert(pMsg); }
void remMsg(Message *const pMsg) { messages.erase(pMsg); }
};
13.37:为Message类添加成员,实现向folders添加或删除一个给定的Folder*。这两个成员类似Folder类的addMsg和remMsg操作。
class Message {
//其余成员
void addFolder(Folder *const fd) { folders.insert(fd); }
void remFolder(Folder *const fd) { folders.erase(fd); }
};
13.38:我们并未使用拷贝并交换方式来设计Message的赋值运算符。你认为原因是什么?
如459页所示,使用swap设计的赋值运算符采用的是值传递的方式传递参数,会引起Message中整个set的拷贝;更重要的是,尽管只是值传递,但是swap仍然会更新到运算符右侧的Message的folders,这显然不是我们所希望的。