C++拷贝控制:拷贝控制实例

拷贝控制实例

​ 虽然通常来说分配资源的类更需要拷贝控制,但资源管理并不是一个类需要定义自己的拷贝控制成员的唯一原因。一个类也需要拷贝控制的帮助来进行薄记工作。


​ 这里我们将概述两个类的设计,这两个类可能用于邮件应用处理中。两个类名为 Message 和 Folder,分别表示电子邮件(或者其他消息)消息和消息目录。每个 Message 对象可以出现在多个 Folder 中。但是,任意给定的 Message 的内容只有一个副本。这样,如果一条 Message 的内容被改变,则我们从它所在的任何 Folder 来浏览此 Message 时,都会看到改变后的内容。

​ 为了记录 Message 位于哪些 Folder 中,每个 Message 都会保存一个所在 Folder 的指针的 set,同样的,每个 Folder 都保存一个它包含的 Message 的指针的 set。

​ Message 提供 save 和 remove 操作,来向一个给定 Folder 添加一条 Message 或是从在删除一条 Message。为了创建一个新的 Message,我们会指明消息内容,但不会指出 Folder。为了将一条 Message 放到一个特定的 Folder 中,我们必须调用 save。

​ 当我们拷贝一个 Message 时,副本和原对象是不同的 Message 对象,但两个 Message 都出现在相同的 Folder 中。

​ 当我们销毁一个 Message 时,我们必须从包含此消息的所有 Folder 中删除指向此 Message 的指针。

​ 当我们将一个 Message 对象赋予另一个 Message 对象时,左侧 Message 的内容会被右侧 Message 的内容所替代。我们还必须更新 Folder 集合,从原来包含左侧 Message 的 Folder 中将它删除,并将它添加到包含右侧 Message 的 Folder 中。

​ Folder 类包含 addMsg 和 remMsg 的成员,分别完成在给定 Folder 对象的消息集合中添加和删除 Message 工作。

Message 类

​ 我们可以编写 Message 类,如下:

class Message {
    friend class Folder;
private:
    std::set<Folder*> folders;		// folders 被隐式初始化为空集合
    std::string contents;       // 消息文本
    void add_to_Folders(const Message&);	// 拷贝构造函数, 拷贝赋值运算符, 析构函数均会使用
    																					// 到,将本 Message 添加到指定参数的 Folder 中
    void remove_from_Folders();			// 从 folders 中的每个 Folder 中删除本 Message
public:
    explicit Message(const std::string& s = ""): contents(s) {}
    Message(const Message&);
    Message&operator= (const Message&);
    void save(Folder&);
    void remove(Folder&);
    ~Message();
};

注意:此函数只有一个构造函数,但其含默认参数,因此它也被当做 Message 的默认构造函数。

save 与 remove 成员
void Message::save(Folder &f) {
    folders.insert(&f);
    f.addMsg(this);
}

void Message::remove(Folder &f) {
    folders.erase(&f);
    f.remMsg(this);
}

不仅要更新 Message 所出现在的 Folder,还要在对应 Folder 中更新 Message。

Mes 类的拷贝控制成员

​ 当我们拷贝一个 Message 时,得到的副本应该与原 Message 出现在相同的 Folder 中。所以,我们需要遍历 Folder 指针的 set,对每个指向原 Message 的 Folder 中添加一个指向新的 Message 的指针。拷贝构造函数和拷贝赋值运算符都需要这个工作,因此我们在 private 中定义一个函数来完成这个公共操作:

void Message::add_to_Folders(const Message &m) {
    for(auto f : m.folders)
        f -> addMsg(this);
}

此例中,我们对 m.folders 中每个 Folder 调用 addMsg。函数 addMsg 会将本 Message 的指针添加到每个 Folder 中。

​ Message 的拷贝构造函数拷贝给定对象的数据成员:

Message::Message(const Message &m): 
	contents(m.contents), folders(m.folders) {
    add_to_Folders(m);
}
Message 的析构函数

​ 当我们销毁一个 Message 时,我们必须从包含此消息的所有 Folder 中删除指向此 Message 的指针。拷贝赋值运算符也要执行此操作,因此我们会定义一个公共函数来完成工作:

void Message::remove_from_Folders() {
    for(auto f : folders)
        f -> remMsg(this);
}

函数 remove_from_Folders 的实现类似 add_to_Folders。有了此函数,编写析构函数就很容易了:

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

剩下的 string, set,编译器会自动调用对应的析构函数。

Message 的拷贝赋值运算符

​ 我们知道,拷贝赋值运算符可能存在自赋值的情况。而这里的 folders 是一个 set,所以我们只能先从左侧运算对象中删除指针,然后再将指针添加到右侧运算对象的 folders 中。

Message& Message::operator=(const Message &rhs) {
    remove_from_Folders();
    contents = rhs.contents;
    folders = rhs.folders;
    add_to_Folders(rhs);
    return *this;
}
Message 的 swap 函数

​ 通过定义一个 Message 特定版本的 swap,我们可以避免对 contents 和 folders 成员进行不必要的拷贝。

void swap(Message &lhs, Message &rhs){
    using std::swap;    // 本例中,严格来说不需要,但是这个是一个好习惯
    for (auto f : lhs.folders)  // 将每个消息的指针从它原来所在的 Folder 中删除
        f->remMsg(&lhs);
    for (auto f : rhs.folders)
        f->remMsg(&rhs);

    swap(lhs.contents, rhs.contents);  // 使用 swap(set&, set&)
    swap(lhs.folders, rhs.folders);    // 使用 swap(string&, string&)

    for (auto f : lhs.folders)    // 将每个 Message 的指针添加到它的新 Folder 中
        f->addMsg(&lhs);
    for (auto f : rhs.folders)
        f->addMsg(&rhs);
}

记一懵点:不明白为什么不调用 remove_from_Folders 与 add_to_Folders

练习 13.37

​ 为 Message 类添加成员,实现向 folders 添加或删除一个给定的 Folder*。这两个成员类似 Folder 的 addMsg 和 remMsg 操作:

    void addFolder(Folder* f) { folders.insert(f); }
    void remFolder(Folder* f){ folders.erase(f); }
总结

​ 注意:save 是 save,它添加 Folder 的同时,也在对应的 Folder 中添加此 Message。而 addFolder 只添加 Folder,并不在对应的 Folder 中添加 Message。虽然这两个函数的行为很类似,而且好像 save 包括了 addFolder,但是两个函数是不同的需求。可能存在只想添加 Folder 的情况 (如修复 bug 这种时候)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值