复制控制(下)

  • 消息处理示例

有些类为了做一些工作需要对复制进行控制。为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序。Message 类和 Folder 类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中。Message 上有 saveremove 操作,用于在指定 Folder 中保存或删除该消息。对每个 Message,我们并不是在每个 Folder 中都存放一个副本,而是使每个 Message 保存一个指针集(set),set 中的指针指向该 Message 所在的 Folder。每个 Folder 也保存着一些指针,指向它所包含的 Message

创建新的 Message 时,将指定消息的内容但不指定 Folder。调用 saveMessage 放入一个 Folder;复制一个 Message 对象时,将复制原始消息的内容和 Folder 指针集,还必须给指向源 Message 的每个 Folder 增加一个指向该 Message 的指针;将一个 Message 对象赋值给另一个,类似于复制一个 Message:赋值之后,内容和 Folder 集将是相同的。首先从左边 Message 在赋值之前所处的 Folder 中删除该 Message。原来的 Message 去掉之后,再将右边操作数的内容和 Folders 集复制到左边,还必须在这个 Folder 集中的每个 Folders 中增加一个指向左边 Message 的指针;撤销一个 Message 对象时,必须更新指向该 Message 的每个 Folder。一旦去掉了 Message,指向该 Message 的指针将失效,所以必须从该 MessageFolder 指针集的每个 Folder 中删除这个指针。//复制(复制构造函数)用来初始化一个空对象,赋值(赋值操作符)则是用来改变已经有内容(已被初始化过的)的对象为新内容。查看这个操作列表,可以看到,析构函数和赋值操作符分担了从保存给定 MessageFolder 列表中删除消息的工作。类似地,复制构造函数和赋值操作符分担将一个 Message 加到给定 Folder 列表的工作。我们将定义一对 private 实用函数完成这些任务。

  • Message Class
class Message {
     public:
         // folders is initialized to the empty set automatically
         Message(const std::string &str = ""):
                       contents (str) { }
         // copy control: we must manage pointers to this Message
         // from the Folders pointed to by folders
         Message(const Message&);//复制构造函数
         Message& operator=(const Message&);
         ~Message();
         // add/remove this Message from specified Folder‘s set of messages
         void save (Folder&);
         void remove(Folder&);
     private:
         std::string contents;      // actual message text
         std::set<Folder*> folders; // Folders that have this Message
         // Utility functions used by copy constructor, assignment, and destructor:
         // Add this Message to the Folders that point to the parameter
         void put_Msg_in_Folders(const std::set<Folder*>&);
         // remove this Message from every Folder in folders
         void remove_Msg_from_Folders();
     };

Message 类定义了两个数据成员:contents 是一个保存实际消息的 stringfolders 是一个 set,包含指向该 Message 所在的 Folder 的指针。构造函数接受单个 string 形参,表示消息的内容。构造函数将消息的副本保存在 contents 中,并(隐式)将 Folderset 初始化为空集。这个构造函数提供一个默认实参(为空串),所以它也可以作为默认构造函数。put_Msg_in_Folders 函数将自身 Message 的一个副本添加到指向给定 Message 的各 Folder 中,这个函数执行完后,形参指向的每个 Folder 也将指向这个 Message。复制构造函数和赋值操作符都将使用这个函数。remove_Msg_from_Folders 函数用于赋值操作符和析构函数,它从 folders 成员的每个 Folder 中删除指向这个 Message 的指针。

  • Message类的复制控制

复制 Message 时,必须将新创建的 Message 添加到保存原 Message 的每个 Folder 中。这个工作超出了合成构造函数的能力范围,所以我们必须定义自己的复制构造函数:

Message::Message(const Message &m):
         contents(m.contents), folders(m.folders)
     {
         // add this Message to each Folder that points to m
         put_Msg_in_Folders(folders);
     }

复制构造函数将用旧对象成员的副本初始化新对象的数据成员。除了这些初始化之外(合成复制构造函数可以完成这些初始化),还必须用 folders 进行迭代,将这个新的 Message 加到那个集的每个 Folder 中。复制构造函数使用 put_Msg_in_Folder 函数完成这个工作。编写自己的复制构造函数时,必须显式复制需要复制的任意成员。显式定义的复制构造函数不会进行任何自动复制。像其他任何构造函数一样,如果没有初始化某个类成员,则那个成员用该成员的默认构造函数初始化。复制构造函数中的默认初始化不会使用成员的复制构造函数。

  • put_Msg_in_Folders成员

put_Msg_in_Folders 通过形参 rhs 的成员 folders 中的指针进行迭代。这些指针表示指向 rhs 的每个 Folder,需要将指向这个 Message 的指针加到每个 Folder。函数通过 rhs.folders 进行循环,调用命名为 addMsgFolder 成员来完成这个工作,addMsg 函数将指向该 Message 的指针加到 Folder 中。

// add this Message to Folders that point to rhs
     void Message::put_Msg_in_Folders(const set<Folder*> &rhs)
     {
         for(std::set<Folder*>::const_iterator beg = rhs.begin();
                                          beg != rhs.end(); ++beg)
             (*beg)->addMsg(this);     // *beg points to a Folder
     }

这个函数中唯一复杂的部分是对 addMsg 的调用:

(*beg)->addMsg(this); // *beg points to a Folder

那个调用以 (*beg) 开关,它解除迭代器引用。解除迭代器引用将获得一个指向 Folder 的指针。然后表达式对 Folder 指针应用箭头操作符以执行 addMsg 操作,将 this 传给 addMsg,该指针指向我们想要添加到 Folder 中的 Message

  • Message赋值操作符

赋值比复制构造函数更复杂。像复制构造函数一样,赋值必须对 contents 赋值并更新 folders 使之与右操作数的 folders 相匹配。它还必须将该 Message 加到指向 rhs 的每个 Folder 中,可以使用 put_Msg_in_Folders 函数完成赋值的这一部分工作。在从 rhs 复制之前,必须首先从当前指向该 Message 的每个 Folder 中删除它。我们需要通过 folders 进行迭代,从 folders 的每个 Folder 中删除指向该 Message 的指针。命名为 remove_Msg_from_Folders 的函数将完成这项工作。对于完成实际工作的 remove_Msg_from_Foldersput_Msg_in_Folders,赋值操作符本身相当简单:

Message& Message::operator=(const Message &rhs)
     {
         if (&rhs != this) {
             remove_Msg_from_Folders(); // update existing Folders
             contents = rhs.contents;   // copy contents from rhs
             folders = rhs.folders;     // copy Folder pointers from rhs
             // add this Message to each Folder in rhs
             put_Msg_in_Folders(rhs.folders);
         }
         return *this;
     }

赋值操作符首先检查左右操作数是否相同。查看函数的后续部分可以清楚地看到进行这一检查的原因。假定操作数是不同对象,调用 remove_Msg_from_Foldersfolders 成员的每个 Folder 中删除该 Message。一旦这项工作完成,必须将右操作数的 contentsfolders 成员赋值给这个对象。最后,调用 put_Msg_in_Folders 将指向这个 Message 的指针添加至指向 rhs 的每个 Folder 中。了解了 remove_Msg_from_Folders 的工作之后,我们来看看为什么赋值操作符首先要检查对象是否不同。赋值时需删除左操作数,并在撤销左操作数的成员之后,将右操作数的成员赋值给左操作数的相应成员。如果对象是相同的,则撤销左操作数的成员也将撤销右操作数的成员!即使对象赋值给自己,赋值操作符的正确工作也非常重要。保证这个行为的通用方法是显式检查对自身的赋值。

  • remove_Msg_from_Folders成员

除了调用 remMsgfolders 指向的每个 Folder 中删除这个 Message 之外,remove_Msg_from_Folders 函数的实现与 put_Msg_in_Folders 类似:

// remove this Message from corresponding Folders
     void Message::remove_Msg_from_Folders()
     {
         // remove this message from corresponding folders
         for(std::set<Folder*>::const_iterator beg =
               folders.begin (); beg != folders.end (); ++beg)
            (*beg)->remMsg(this); // *beg points to a Folder
     }
  • Message析构函数

剩下必须实现的复制控制函数是析构函数:

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

有了 remove_Msg_from_Folders 函数,编写析构函数将非常简单。我们调用 remove_Msg_from_Folders 函数清除 folders,系统自动调用 string 析构函数释放 contents,自动调用 set 析构函数清除用于保存 folders 成员的内存,因此,Message 析构函数唯一要做的是调用 remove_Msg_from_Folders。赋值操作符通常要做复制构造函数和析构函数也要完成的工作。在这种情况下,通用工作应在 private 实用函数中。

时间: 2024-10-12 15:37:14

复制控制(下)的相关文章

第十九篇:复制控制( 下 ) --- 自定义析构函数

前言 经过前两篇随笔( 上  中 )的分析我们已经解决了具有指针成员的同类对象“ 干涉 ”问题.可惜,前面给出的解决方案代码还是不完整.还有什么问题呢?观察发现,构造函数里面有new的关键字出现,也就是说开辟了新的内存空间,我们也知道new必须也只能对应一个delete,而不应该让系统自己处理( 还不熟练new和delete用法的点这里 ),但这里和new对应的delete去哪里了? 解决思路 应该何时delete?显然,应该在对象销毁的时候delete.C++中定义了这样一种函数 --- 析构

C++复制控制

1.复制构造函数可用于: (1)根据另一个同类型的对象显示或隐式初始化一个对象 string str1="test";   //隐式 string str2=str1; //显示 str1为先调用string的字符串形参的构造函数,创建一个临时对象,然后,使用string复制构造函数将str1初始化为那个临时对象的副本. (2)复制一个对象,将它作为实参传给一个函数 (3)从函数返回时复制一个对象 当形参为非引用类型的时候,将复制实参的值.类似地,以非引用类型作返回值时,将返回retu

C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承

面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针/引用转换为基类子对象的指针/引用. 基类类型对象既能够作为独立对象存在,也能够作为派生类对象的一部分而存在,因此,一个基类对象可能是也可能不是一个派生类对象的部分,因此,没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自己主动)转换. 关于对象类型,尽管一般能够使用派生类型的对象对基类

C++ Primer 学习笔记_68_面向对象编程 --构造函数和复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

【C++ Primer】复制控制

十三.复制控制 1. 复制构造函数 类中的成员函数都默认为inline类型,所以即使在类定义体内的函数声明显示定义为inline类型,在进行函数定义时也能够将inline进行省略. // 复制构造函数应该为常量引用类型,如果允许传值参数会造成无限循环调用从而导致内存溢出. CopyConstruct(const CopyConstruct& a){value = a.value;} 复制构造函数可用于初始化顺序容器中的元素,如vector<string> svec(5); 这种方式使用

C++独孤九剑第五式——人生几何(对象复制控制)

对酒当歌,人生几何? 譬如朝露,去日苦多. 人的一生可能惊涛骇浪,更可能波澜不惊,这次我们就来探讨一下"对象"(当然各位同学自己的对象不在本次讨论范围之内O(∩_∩)O,课后自己讨论吧)一生的"起起落落",即对象的复制控制. 复制控制包括三个部分:复制构造函数的调用.赋值操作符的调用.析构函数的调用.下面就这三个操作来逐一进行介绍,大家共同学习(*^-^*) 一.复制构造函数 复制构造函数:首先它也是构造函数,所以函数名与类名相同,没有返回值:其次,它只有一个形参,

C++ Primer 学习笔记_68_面向对象编程 -构造函数跟复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

C++ Primer 学习笔记_67_面向对象编程 -变换与继承、复制控制与继承

面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每个派生类对象都包含一个基类部分,因此可以像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,可以将派生类对象的指针/引用转换为基类子对象的指针/引用. 基类类型对象既可以作为独立对象存在,也可以作为派生类对象的一部分而存在,因此,一个基类对象可能是也可能不是一个派生类对象的部分,因此,没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自动)转换. 关于对象类型,虽然一般可以使用派生类型的对象对基类类型的

C++primer第十三章 复制控制

每种类型还定义了创建该类型的对象时会发生什么——构造函数定义了该类类型对象的初始化.类型还能控制复制.赋值或撤销该类型的对象时会发生什么——类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制这些行为. 复制构造函数是一种特殊构造函数,具有单个形参,该形参(常用 const 修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数.当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数. 析构函数是构造函数的互补:当对象