C++ Primer 学习笔记_56_ 类和数据抽象 --消息处理演示示例
拷贝控制
--消息处理演示样例
说明:
有些类为了做一些工作须要对复制进行控制。
为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序。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_ 类和数据抽象 --消息处理演示示例的更多相关文章
- C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员
复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...
- C++ Primer 学习笔记_53_类和数据抽象 --友元、static员
分类 --友元.static成员 一.友元 友元机制同意一个类将对其.友元关系:一个样例 如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据.Screen应该同意其 ...
- C++ Primer 学习笔记_54_类和数据抽象 --拷贝构造函数、赋值运算符
拷贝控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,须要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用! 复制构造函数: ...
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- Java学习笔记——File类之文件管理和读写操作、下载图片
Java学习笔记——File类之文件管理和读写操作.下载图片 File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图 ...
- python学习笔记4_类和更抽象
python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...
- Java学习笔记之---类和对象
Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态 例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...
- UML学习笔记:类图
UML学习笔记:类图 有些问题,不去解决,就永远都是问题! 类图 类图(Class Diagrame)是描述类.接口以及它们之间关系的图,用来显示系统中各个类的静态结构. 类图包含2种元素:类.接口, ...
随机推荐
- 判断闰年(go语言版本)
import "strconv" func IsLeapYear(y string) bool { //y == 2000, 2004 //判断是否为闰年 year, _ := s ...
- 检验身份证的正确性(C语言版本)
/* check id_card * write by sndnvaps<sndnvaps@gmail.com> * ai -> a1 , a2, a3, a4, a5, a6... ...
- [ACM] POJ 3254 Corn Fields(状态压缩)
Corn Fields Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 8062 Accepted: 4295 Descr ...
- sublime配置攻略
大家好,今天给大家分享的编辑器:sublime text2 我用过非常多编辑器, EditPlus.EmEditor.Notepad++.Notepad2.UltraEdit.Editra.V ...
- Linux(Centos)中tcpdump参数用法详解(转)
在linux下进行编程开发的人尤其是网络编程的人会经常需要分析数据包,那么一定会用到tcpdump,下面就是关于tcpdump的使用方法说明(1). tcpdump的选项 -a 将网络地址 ...
- 关于csrss.exe和winlogon.exe进程多、占用CPU高的解决办法
原地址 http://blog.sina.com.cn/s/blog_912e77480101nuif.html 最近VPS的CPU一直处在100%左右,后台管理上去经常打不开,后来发现上远程都要 ...
- 【C语言探索之旅】 第二部分第五课:预处理
内容简介 1.课程大纲 2.第二部分第五课: 预处理 3.第二部分第六课预告: 创建你自己的变量类型 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语 ...
- QtQuick桌面应用程序开发指南 4)动态管理Note对象_B 5)加强外观 6)许多其他的改进
4.2.2 Stateless(不管状态)JavaScript库 为了让开发轻松点, 使用一个JavaScript接口来和数据库交互是个好主意, 它在QML中提供了方便的方法; 在QtCreator中 ...
- mysql经常使用命令总结
MySQL经常使用指令(备查) 最经常使用的显示命令: 1.显示数据库列表. show databases; 2.显示库中的数据表: use mysql; show tables; 3.显示数 ...
- Android-用你自己的自定义图像资源(2)
Android-自己定义图像资源的使用 2014年4月29日 上一篇博客.介绍前面几种图像资源的使用,本篇博客把剩下的所有介绍完: 普通图像资源 XML图像资源 Nine-patch图像资源 XM ...