拷贝控制

--消息处理演示样例

说明:

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

为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序。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. 《数据结构、算法及应用》9.(C++实施订单)

    最近阅读<数据结构.算法及应用>这本书,书中的习题汇总,用自己的方法来实现这些问题.可能效率.等方面存在着非常多的问题,也可能是错误的实现.假设大家在看这本书的时候有更优更好的方法来实现, ...

  2. Gas Station [leetcode] 两个解决方案

    因为gas的总数大于cost总时间.你将能够圈住整个城市. 第一溶液: 如果一開始有足够的油.从位置i出发.到位置k时剩余的油量为L(i,k). 对随意的k.L(i,k)依据i的不同,仅仅相差常数. ...

  3. linux下一个Oracle11g RAC建立(五岁以下儿童)

    linux下一个Oracle11g RAC建立(五岁以下儿童) 四.建立主机之间的信任关系(node1.node2) 建立节点之间oracle .grid 用户之间的信任(通过ssh 建立公钥和私钥) ...

  4. Intent用法

    Intent是android系统中的最佳男主角,Intent翻译成中文的意思是"意图",说白了就是"我想要...",也就是说眼下运行中的Activity想要请其 ...

  5. [TroubleShooting] The server network address can not be reached or does not exist

    Backtround: I'm trying to set up mirroring between two sql 2008 R2 databases on different servers in ...

  6. Build制作模型

    #include <iostream> using namespace std; //不知道为什么事实上非常好解释的东西在网上搞的人晕头转向的,下面是我的理解. //一个基类衍生出很多详细 ...

  7. org.eclipse.birt.report.data.oda.jdbc.JDBCException: Missing properties in Connection.open(Propertie

    首先查看project的web.xml档"BIRT_RESOURCE_PATH"属性的设置.此属性设置的是"用户资源存放路径.这些资源包含 library 文件,imag ...

  8. S2SH新手框架结构的准备工作只需要导入这些文件

    实习北京最近一直在某公司.时间很冲突,总想着有事找东西坐,思想,做事先成套我吧 一套完整的东西想好主题,术去实现了,在学校尽管说是学了J2EE,可是确实没学到东西,Struts2+Hibernate不 ...

  9. Eclipse build launcher 3具体步骤

    1. 下载launcher 3源代码  (需要FQ) git clone https://android.googlesource.com/platform/packages/apps/Launche ...

  10. java 产生的固体物的基础上 增删改的SQL声明

    经过多次修改.最后版本. package com.power.sql; import java.lang.reflect.Field; import java.lang.reflect.Modifie ...