C++ Primer 消息处理示例

  1. 题目要求:
    C++ Priemer 消息处理示例: Message类和Folder类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中。Message上有Save和Remove操作,用于指定Folder中保存或删除消息。 对每个Message,我们并不是在每个Folder中都存放一个副本,而是是每个Message保存一个指针集(set),set中的指针指向给Message所在的Folder。每个Folder也保存着一些指针,指向它所包含的Message。
  2. 分析:
    起初,我觉得很难理解,没有头绪。后来找到一个窍门:
    设计程序的一个良好习惯是首先将程序所涉及的操作列出来。明确需要的操作有助于建立需要的数据结构和实现这些行为。
    所以,在下面的程序中,我详细罗列了Message类的每一个操作,以及操作的分步完成,也许,这有助于理解。至于Folder类,与Message大同小异。所以,在下面的程序中我建议你先看Message类,这样比较容易理解。
  3. 程序
    #include <set>
    #include <string>
    
    /****************以下是Folder类的完全声明***************************/
    //编写Folder类,数据成员是指向Message类的指针集
    class Message;//前向声明,用于在Folder中定义指向Message的指针
    class Folder
    {
    private:
    	std::set<Message*> messages;//该目录的消息集
    	//复制控制成员所用到的实用函数:
    	//将目录加到形参所指的消息集中
    	void put_Fldr_in_Messages(const std::set<Message*>&);
    	//从目录所含的消息集中删除该目录
    	void remove_Fldr_in_Messages();
    public:
    	//默认构造函数
    	Folder(){};
    	//复制控制成员
    	Folder(const Folder&r);
    	Folder& operator = (const Folder&);
    	~Folder();
    	//在指定Message的目录集中添加/删除该目录
    	void save(Message&);
    	void remove(Message&);
    	//在该目录的消息集中添加/删除指定消息
    	void addMsg(Message*);
    	void remMsg(Message*);
    };
    /****************以上是Folder类的完全声明***************************/
    
    /***************以下是Message类的完全声明***************************/
    class Message
    {
    private:
    	std::string contents;//实际消息文本
    	std::set<Folder*> folders;//包含该消息的目录
    	//复制构造函数、赋值操作符和析构函数所使用的实用函数:
    	//将消息加到形参所指的目录集中,即在目录中添加指向该消息的指针
    	void put_Msg_in_Folders(const std::set<Folder*>&);
    	//从消息所在的目录中删除该消息,即删除指向该消息的指针
    	void remove_Msg_from_Folders();
    public:
    	//folders自动初始化为空集
    	Message(const std::string &str=""):
    	  contents(str){}
    	//复制控制成员
    	  Message(const Message&);
    	  Message& operator=(const Message&);
    	  ~Message();
        //save操作,在指定Folder的消息集中添加该消息
    	  void save(Folder&);
    	//remove操作,在指定Folder的消息集中删除该消息
    	  void remove(Folder&);
    	//在包含该消息的目录集(set<Folder*> folders)中添加/删除Folder指针
    	  void addFldr(Folder*);
    	  void remFldr(Folder*);
    };
    /***************以上是Message类的完全声明***************************/
    
    /***************以下是Folder类成员函数的定义************************/
    //复制构造函数,假设将目录fldr1复制给fldr2:
    //1.内容复制:将目录fldr1的内容复制给fldr2,可以自动完成
    //2.在目录fldr1包含的消息集中添加指向fldr2的指针
    Folder::Folder(const Folder &f):
    messages(f.messages)
    {
    	put_Fldr_in_Messages(f.messages);
    }
    //赋值操作符,假设将目录fldr3赋值给fldr4:
    //1.删除目录fldr4:在fldr4包含的消息集中删除指向fldr4的指针
    //2.内容复制:将fldr3的内容复制给fldr4
    //3.添加目录:在fldr3包含的消息集中添加指向fldr4的指针
    Folder& Folder::operator=(const Folder &rhs)
    {
    	if(&rhs!=this)
    	{
    		remove_Fldr_in_Messages();
    	}
    	return *this;
    }
    //析构函数
    //1.删除目录内容,自动完成
    //2.在该目录所包含的消息集中删除指向该目录的指针
    Folder::~Folder()
    {
    	remove_Fldr_in_Messages();
    }
    //将目录加到形参所指的消息集中,即要在Message类的set<Folder*>中添加
    //指向该目录的指针元素,这需要调用类Message的成员函数addFldr()
    void Folder::put_Fldr_in_Messages(const std::set<Message*> &rhs)
    {
    	for(std::set<Message*>::const_iterator beg=rhs.begin();
    		beg!=rhs.end();++beg)//循环遍历形参所指的消息集
    		(*beg)->addFldr(this);
    }
    //从目录所含的消息集中删除该目录,即在Message类的set<Folder*>中删除
    //指向该目录的指针,这需要调用类Message的成员函数remFldr()
    void Folder::remove_Fldr_in_Messages()
    {
    	for(std::set<Message*>::const_iterator beg=messages.begin();
    		beg!=messages.end();++beg)
    		(*beg)->remFldr(this);
    }
    //save操作
    void Folder::save(Message &msg)
    {
    	addMsg(&msg);
    	msg.addFldr(this);//更新相应的消息
    }
    //remove操作
    void Folder::remove(Message &msg)
    {
    	remMsg(&msg);
    	msg.remFldr(this);//更新相应的消息
    }
    //接受一个指向消息Message的指针,并添加消息集set<Message*>中
    	  void Folder::addMsg(Message *msg)
    	  {
    		  messages.insert(msg);
    	  }
    //接受一个指向消息Message的指针,并在消息集set<Message*>中删除该指针
    	  void Folder::remMsg(Message *msg)
    	  {
    		  messages.erase(msg);
    	  }
    /***************以上是Folder类成员函数的定义************************/
    
    /***************以下是Message类成员函数的定义************************/
    //复制构造函数,假设将消息Ms1复制给新消息Ms2,j即Ms2(Ms1):
    //1.将Ms1的内容复制复制给Ms2
    //2.在消息Ms1所属目录集中添加指向消息Ms2的指针
    Message::Message (const Message &m):
    contents(m.contents),folders(folders)//复制内容
    {
    	put_Msg_in_Folders(m.folders);//调用函数在目录集中添加指向新消息的指针
    }
    //赋值操作符,假设将消息Ms3赋值给Ms4,即Ms4=Ms3:
    //1.首先删除Ms4所属目录集中指向Ms4的指针
    //2.然后将Ms3的内容复制给Ms4
    //3.最后,在Ms3所属的目录集中添加指向Ms4的指针
    Message& Message::operator=(const Message &rhs)
    {
    	if(&rhs!=this)//两个消息不相等,赋值才有意义
    		          //这里的&rhs是取rhs对象的地址,而不是rhs的引用
    				  //因为this就是一个指向对象的指针,它的值就是所指对象的地址
    	 {
    		//从消息所在的目录集中删除该消息,即删除指向该消息的指针
    	     remove_Msg_from_Folders();
    	    //复制rhs中的内容
    		contents=rhs.contents;
    		folders=rhs.folders;
    		//将消息加到形参所指的目录集中
    		put_Msg_in_Folders(rhs.folders);
    	}
    	return *this;
    }
    /******************************************************************************
     为什么在复制构造函数不首先删除Ms2所属目录集中指向Ms2的指针,而赋值操作符中却要
     首先删除Ms4所属目录集中指向Ms4的指针呢?这就涉及到复制构造函数和赋值操作符的区别:
     复制构造函数用于产生新对象,给新对象初始化。故Ms2是新对象,它不属于任何目录集。
     而赋值操作符则是用于两个本已存在的对象,故需要首先删除。
     ******************************************************************************/
    //析构函数:
    //1.删除Message的内容,可以自动完成
    //2.在消息所属的目录集中删除该消息,即删除指向该消息的指针
    Message::~Message()
    {
    	void remove_Msg_from_Folders();
    }
    //将消息加到形参所指的目录集中,即在目录中添加指向该消息的指针
    void Message::put_Msg_in_Folders(const std::set<Folder*> &rhs)
    {
    	for(std::set<Folder*>::const_iterator beg=rhs.begin();
    		beg!=rhs.end();++beg)//循环遍历形参所指的目录集
    		(*beg)->addMsg(this);//在目录中添加指向该消息的指针:
    	                         //beg是指向消息所属的目录的指针,*beg就是该目录
    	                         //它属于类Folder,然后调用用目录成员函数addMsg
    }
    //在消息所属的目录集中删除该消息,即删除指向该消息的指针
    void Message::remove_Msg_from_Folders()
    {
    	for(std::set<Folder*>::const_iterator beg=folders.begin();
    		beg!=folders.end();++beg)//遍历消息所属的目录集
    		(*beg)->remMsg(this);
    }
    //save操作,在指定目录的消息集中添加该消息:
    //1.将该目录添加到Message的目录集中,即在Message的目录集set<Folder*>添加一个指针,
    //  指向该目录
    //2.更新目录以反映它指向该Message,即在目录中添加一个指向该Message的指针
    void Message::save(Folder &fldr)
    {
    	addFldr(&fldr);//1.调用消息类Message的成员函数addFldr()添加指向目录的指针
    	fldr.addMsg(this);//2.调用目录类Folder的成员函数addMsg()添加指向消息的指针
    }
    //remove操作,在指定Folder的消息集中删除该消息
    //1.在消息所属目录集中删除该目录,即在set<Folder*>中删除指向该目录的指针
    //2.更新目录:在目录中删除指向该消息的指针
    	  void Message::remove(Folder &fldr)
    	  {
    		  remFldr(&fldr);//1.调用消息类Message的成员函数remFldr()删除指向目录的指针
    		  fldr.remMsg(this);//2.调用目录类Folder的成员函数remMsg()删除指向该消息的指针
    	  }
    //接受一个指向目录Folder的指针,并添加目录集set<Folder*>中
    	  void Message::addFldr(Folder *fldr)
    	  {
    		  folders.insert(fldr);
    	  }
    //接受一个指向目录Folder的指针,并在目录集set<Folder*>中删除该指针
    	  void Message::remFldr(Folder *fldr)
    	  {
    		  folders.erase(fldr);
    	  }
    /***************以上是Message类成员函数的定义************************/
  4. 总结
    1)在定义成员函数时,我总是搞不清楚应不应该传递形参,应该传递怎样的形参。
          方法:详细而准确的描述一个操作,并根据操作明确所需的数据和算法。而且有助于确定需不需要从外部传递参数,传递什么参数,这些可以从描述的一些定语中知       道。
    2)  Message类和Folder类的依赖关系,这决定了两个类声明的先后顺序。
         在写程序代码时,你会发现,Message类对象会调用Folder类的函数,但这时如果没有声明Folder类,而且必须是完全声明,否则就无法调用。
    3)什么时候应该定义自己的复制构造函数,赋值操作符和析构函数?
          一般这三者会同时出现,让我们以复制构造函数为例。
         在以下两种情况下,经常需要定义自己的复制构造函数:
          i)类有一个数据成员是指针,需要动态分配内存。
          ii)必须做一些特定的工作。
          第一种情况很好理解,而第二种情况,什么叫“特定的工作”,让我们以Message类的复制构造函数为例。它不仅需要复制内容,还要在消息所属的目录集中添加指向       该消息的指针,这是合成复制构造函数无法完成的,这就是“特定的工作”,即需要在复制构造函数中完成,而合成复制构造函数有无法完成的工作。
    4)如果复制控制成员中有一些共同的操作,可以将此操作提取出来,写成private函数,在复制控制成员中分别调用,这样就避免了重复代码。如本程序:
          复制构造函数和赋值操作符都有“在每个Folder中增加一个指向Message的指针”操作,我们就写了private函数put_Msg_in_Folders(const std::set<Folder*>&)函数;
          赋值操作符和析构函数都有在Folder中删除指向Message的指针,所以我们就写了private函数void remove_Msg_from_Folders();


         


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值