拷贝控制

--消息处理演示样例

说明:

有些类为了做一些工作须要对复制进行控制。

为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序。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);
}

C++ Primer 学习笔记_56_ 类和数据抽象 --消息处理演示示例的更多相关文章

  1. C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员

    复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...

  2. C++ Primer 学习笔记_53_类和数据抽象 --友元、static员

    分类 --友元.static成员 一.友元 友元机制同意一个类将对其.友元关系:一个样例 如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据.Screen应该同意其 ...

  3. C++ Primer 学习笔记_54_类和数据抽象 --拷贝构造函数、赋值运算符

    拷贝控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,须要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数: ...

  4. C++ Primer学习笔记(三) C++中函数是一种类型!!!

    C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...

  5. C++ Primer学习笔记(二)

    题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接  C++ Primer学习笔记(一)   27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...

  6. Java学习笔记——File类之文件管理和读写操作、下载图片

    Java学习笔记——File类之文件管理和读写操作.下载图片 File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图 ...

  7. python学习笔记4_类和更抽象

    python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...

  8. Java学习笔记之---类和对象

    Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态  例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...

  9. UML学习笔记:类图

    UML学习笔记:类图 有些问题,不去解决,就永远都是问题! 类图 类图(Class Diagrame)是描述类.接口以及它们之间关系的图,用来显示系统中各个类的静态结构. 类图包含2种元素:类.接口, ...

随机推荐

  1. WebCollector 2.x 新手教程

    WebCollector爬虫官网:https://github.com/CrawlScript/WebCollector 技术讨论群:250108697 WebCollector 2.x教程列表 We ...

  2. [Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)

    ---------------------------------------------------------------------------------------------------- ...

  3. hdu 4920 Matrix multiplication(矩阵乘法)2014多培训学校5现场

    Matrix multiplication                                                                           Time ...

  4. hdu 1150 Machine Schedule(最小顶点覆盖)

    pid=1150">Machine Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/327 ...

  5. 1号店Interview小结

    三大范式: 为了建立冗余较小.结构合理的数据库,设计数据库时必须遵循一定的规则.在关系型数据库中这种规则就称为范式.范式是符合某一种设计要求的总结.要想设计一个结构合理的关系型数据库,必须满足一定的范 ...

  6. Android应用开发:LoaderManager在Activity/Fragment中的使用分析

    LoaderManager 外部接口initLoader:起始 public <D> Loader<D> initLoader(int id, Bundle args, Loa ...

  7. 在python正在使用mysql

    缘由 近期在折腾一个小东西须要抓取网上的页面.然后进行解析.将结果放到数据库中. 了解到Python在这方面有优势,便选用之. 由于我有台server上面安装有mysql,自然使用之.在进行数据库的这 ...

  8. MySQL列:innodb的源代码的分析的基础数据结构

    在过去的一年中的数据库相关的源代码分析.前段时间分析levelDB实施和BeansDB实现,数据库网络分析这两篇文章非常多.他们也比较深比较分析,所以没有必要重复很多劳力.MYSQL,当然主要还是数据 ...

  9. Java Web整合开发(14) -- Struts 1.x 概述

    整合Spring与Struts1的三种方法总结 无论用那种方法来整合,第一步就是要装载spring的应用环境,有三种方式: #1. struts-config.xml <?xml version ...

  10. ASP.NET MVC4实现URL伪静态

    1.在Web.config添加节点配置: <system.webServer> <modules runAllManagedModulesForAllRequests="t ...