1、两个类里边分别保存一个对方的set表,当前类有拷贝或者销毁时需要更新另一个类的set表。
2、两个类都需要访问对方的private成员,所以两互相为友元,这样的两个类必须声明在同一个".h"文件中否则会导致先编译的类使用了使用的另一类是不完全的。
分开在两个".h"文件中定义为出现如下结果:
A.h
class B; class A{ friend class B; ... }
A.cpp
void A::func_access_B{ 访问B中的private成员 // 这个时候B还是不完全类,访问B的成员是错误的 }
B.h
void A::func_access_B{ class B { friend class A; ... }
B.cpp
void A::func_access_B{ void B::func_access_A { 访问A中的private成员 // 这个时候A已经完全声明过,访问A的成员是正确的。 }
两个在一个头文件中声明,一个文件中定义
A_B.h
class B; class A { friend class B; ... } class B { friend class A; ... }
A_B.cpp
void A::func_access_B { 访问B中的private成员 // 这个时候B已经完全声明过,访问B的成员是正确的。 } void B::func_access_A { 访问A中的private成员 // 这个时候A已经完全声明过,访问A的成员是正确的。 }
Message_Folder.h
#include <set> #include <string> #include <iostream> using namespace std; #ifndef MESSAGE_FOLDER__H #define MESSAGE_FOLDER__H class Folder; class Message { friend class Folder; friend void swap(Message &, Message &); friend void swap(Folder &, Folder &); public: typedef enum {ADD = 0, REMOVE} update_mode; Message(const string &msg = ""):contents(msg) {} Message(const Message &msg); Message &operator=(const Message &); ~Message(); void save(Folder &); void remove(Folder &); string getContents() {return contents;} void setContents(const string &c) {contents = c;} void display(); private: string contents; set<Folder*> folders; void update_folder_set(const update_mode &); }; void swap(Message &, Message &); class Folder { friend class Message; friend void swap(Message &, Message &); friend void swap(Folder &, Folder &); public: typedef enum {ADD = 0, REMOVE} update_mode; // 这个与Message中定义的enum不冲突,因为两个的作用域都只在类中。 Folder(const string &n = ""):name(n) {} Folder(const Folder &); Folder &operator=(const Folder &); ~Folder(); void save(Message &); void remove(Message &); string getName() {return name;} void setName(const string &n) {name = n;} void display(); private: string name; set<Message*> messages; void update_folder_set(const update_mode &); }; void swap(Folder &, Folder &); #endif
Message_Folder.cpp
#include "Message_Folder.h" //--------------- Message Part --------------// // 将message(this)加入到某个Folder中去 // 这里要更新message(this)的set<Folder*>表和Folder中的set<Message*>表 void Message::save(Folder &f) { folders.insert(&f); // 更新set<Folder*>表,指示这条message存在某个Folder中。 f.messages.insert(this); // 更新set<Message*>表,指示folder中有这条消息。 } // 将message(this)从某个Folder中去除 // 这里要更新message(this)的set<Folder*>表和Folder中的set<Message*>表 void Message::remove(Folder &f) { folders.erase(&f); f.messages.erase(this); } // Message在拷贝和析构操作的时候,会复制一条message,或者销毁一条message // message复制和销毁要同步更新这条message存在的folder的set<Message*>表 // 复制消息的时候,Folder中新增message,析构消息的时候Folder中删除消息。 void Message::update_folder_set(const update_mode &mode) { if(mode == ADD) { for(const auto &v : folders) { v->insert(this); } } else { for(const auto &v : folders) { v->erase(this); } } } // Message的拷贝构造函数 // Message拷贝后,拷贝的消息要出现在对应的Folder中 // 就需要更新原消息所在Folder,将新消息(this)加入到对应的Folder中 Message::Message(const Message &msg) : contents(msg.contents), folders(msg.folders) { update_folder_set(ADD); } // Message的拷贝赋值运算符 // Message在拷贝赋值的时候,左侧对象被覆盖(对应contents来说就是不存在了),要更新左侧对象对象所在Folder的set<Message*>表 // 左侧对象被右侧对象覆盖,左侧对象的set<Folder*>也被覆盖,这样要根据拷贝的set<Folder*>更新Folder中的set<Message*>表 Message &Message::operator=(const Message &msg) { update_folder_set(REMOVE); folders = msg.folders; contents = msg.contents; update_folder_set(ADD); return *this; } // Message的析构函数 // Message析构后,消息不存在,所以要更新消息所在Folder的set<Message*> Message::~Message() { update_folder_set(REMOVE); } // Message的交换函数 // 理论上是在vector<Message>调用sort的时候会调用这个函数,但是测试没有调用 // message交换了,也就是原对象对的message变了,这个时候先要销毁原来message对应的folder联系 // message交互后再重新建立message与folder的联系。 void swap(Message &lhs, Message &rhs) { for(const auto &v : lhs.folders) v->messages.erase(&lhs); for(const auto &v : rhs.folders) v->messages.erase(&rhs); swap(lhs.contents, rhs.contents); swap(lhs.folders, rhs.folders); for(const auto &v : lhs.folders) v->messages.insert(&lhs); for(const auto &v : rhs.folders) v->messages.insert(&rhs); } void Message::display() { for(const auto &v : folders) cout<<v->getName()<<endl; } //--------------- Folder Part --------------// void Folder::save(Message &msg) { folders.insert(&msg); msg.folders.insert(this); } void Folder::remove(Message &f) { messages.erase(&msg); msg.folders.erase(this); } void Folder::update_message_set(const update_mode &mode) { if(mode == ADD) { for(const auto &v : messages) { v->insert(this); } } else { for(const auto &v : messages) { v->erase(this); } } } Folder::Folder(const Folder &f) : name(f.name), messages(f.messages) { update_message_set(ADD); }
测试程序
Message msg1("msg1"); Message msg2("msg2"); Folder fld1("folder1"); Folder fld2("folder2"); // 测试message类的操作更新folder类的联系(更新folder中的set表) msg1.save(fld1); // 将msg1保存到folder1中。 cout<<"folder messages ..1"<<endl; fld1.display(); // 输出msg1,folder1中有一条消息 Message msg3(msg1); // 用msg1拷贝构造msg3,msg1在Folder1中,msg3也会在folder1中 msg3.setContents("msg3"); // 将拷贝的contents="msg1",修改为"msg3"方便区分 cout<<"folder messages ..2"<<endl; fld1.display(); // 输出msg1,msg3 Message msg4; msg4 = msg1; // msg4为msg1的拷贝,msg4也会在Folder1中 msg4.setContents("msg4"); cout<<"folder messages ..3"<<endl; fld1.display(); // 输出msg1,msg3,msg4 Message *msg5 = new Message(msg1); // 用msg1拷贝构造msg3,msg1在Folder1中,msg3也会在folder1中 msg5->setContents("msg5"); cout<<"folder messages ..4"<<endl; fld1.display(); // 输出msg1,msg3,msg4,msg5 delete msg5; // 销毁msg5,msg5与folder1自动断开联系,即msg5从fld1中去除。 cout<<"folder messages ..5"<<endl; fld1.display(); // 输出msg1,msg3,msg4 msg1.remove(fld1); // msg1从fld1中去除,手动断开联系 cout<<"folder messages ..6"<<endl; fld1.display(); // 输出msg3,msg4 // 测试folder类的操作更新message类的联系(更新message中的set表) fld2.save(msg1); // 将msg1保存到folder2中,现在msg1只出现在folder2中 cout<<"message1 folder ..1"<<endl; msg1.display(); // 输出fld2 Folder fld3(fld2); // 用fld2拷贝构造fld3,fld2包含了msg1,那么fld3也要包含msg1,即msg1会被包含在fld3中。 fld3.setName("fld3"); // 修改fld3的名字,便于区分 cout<<"message1 folder ..1"<<endl; msg1.display(); // 输出fld2,fld3 Folder fld4; fld4 = fld2; // fld4为fld2的拷贝,fld4也要包含msg1 fld4.setName("fld4"); cout<<"message1 folder ..1"<<endl; msg1.display(); // 输出fld2,fld3,fld4 Folder *fld5 = new Folder(fld2); fld5->setName("fld5"); cout<<"message1 folder ..1"<<endl; msg1.display(); // 输出fld2,fld3,fld4,fld5 delete fld5; cout<<"message1 folder ..1"<<endl; msg1.display(); // 输出fld2,fld3,fld4 fld2.remove(msg1); cout<<"message1 folder ..1"<<endl; msg1.display();