C++类的拷贝控制demo
2022/2/8 17:42:28
本文主要是介绍C++类的拷贝控制demo,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
拷贝控制
有时候我们需要两个类对象互相关联,当其中一个对象修改后也要关联修改另一个,用这个例子说明拷贝控制的案例。我们有两个类,Message类表示信息类,Folder类表示文件夹类,Message类里有成员folders表示其所属于哪些文件夹。Folder类有成员messages表示其包含哪些messages,所以Folder和Message之间是互相包含,多对多的关系。
同时我们要考虑Message类的拷贝,赋值,销毁等操作,如何同步处理其关联的Folder类。
其关系图是这样的
class Message { friend class Folder; public: // folder被隐式初始化为空集合 explicit Message(const std::string &str = "") : contents(str) {} // 拷贝控制成员,用来管理指向本Message的指针 Message(const Message &); // 拷贝赋值运算符 Message &operator=(const Message &); //析构函数 ~Message(); //将Message保存在指定Folder中 void save(Folder &); //从Folder中删除Message void remove(Folder &); private: // 消息内容 std::string contents; // 消息所属文件夹 std::set<Folder *> folders; //将本Message添加到参数msg的folder中 void add_to_Folders(const Message &msg); //从folders中的每个Folder删除本Message void remove_from_Folders(); };
Message类定义了构造函数,默认将本Message所属的Folder集合设置为空。同时提供了save和remove操作,将本Message保存给指定Folder以及从指定Folder中删除。两个私有函数在很多地方通用,所以提出来作为私有函数。
同样我们声明Folder类
class Folder { friend class Message; public: explicit Folder(const std::string &nm = "") : name(nm) {} //拷贝控制成员 Folder(const Folder &); //拷贝赋值运算符 Folder &operator=(const Folder &); //析构函数 ~Folder(); //保存指定的msg void addMsg(Message &); //移除指定的msg void remMsg(Message &); private: //文件夹名字 std::string name; //包含的消息列表 std::unordered_map<std::string, Message *> msgs; };
接下来我们实现Message类的添加和删除操作
//将Message保存在指定Folder中 void Message::save(Folder &f) { //将文件夹f添加到msg的folders里 this->folders.insert(&f); //将本msg添加到folder中 f.addMsg(*this); } //从Folder中删除Message void Message::remove(Folder &f) { //将文件夹从msg的folders里删除 this->folders.erase(&f); f.remMsg(*this); }
接下来我们实现folder的addMsg和remMsg操作
//保存指定的msg void Folder::addMsg(Message &msg) { this->msgs.insert(make_pair(msg.contents, &msg)); } //移除指定的msg void Folder::remMsg(Message &msg) { this->msgs.erase(msg.contents); }
上述代码完成了msg插入folder后两个类之间的关联逻辑,当msg之间拷贝构造时需要完成folder的拷贝
//将本Message添加到参数msg的folder中 void Message::add_to_Folders(const Message &msg) { for (auto f : msg.folders) { f->addMsg(*this); } } //拷贝构造函数将m的folders拷贝给自己 Message::Message(const Message &m) { contents = m.contents; folders = m.folders; add_to_Folders(m); }
拷贝构造函数就是将参数message的成员拷贝给新生成的对象,然后通过add_to_Folders函数将本消息添加到m的folders中。
接下来实现析构函数,message析构时将folders遍历移除本message
//从folders中的每个Folder删除本Message void Message::remove_from_Folders() { for (auto f : folders) { f->remMsg(*this); } } //析构函数 Message::~Message() { remove_from_Folders(); }
拷贝构造函数需要两个操作,将自身的folders中删除本msg,然后将=右侧的message的folders赋值给本msg,并且将本message添加到folders中。其实是融合了析构和拷贝构造的两个操作。
// 拷贝赋值运算符 Message &Message::operator=(const Message &msg) { remove_from_Folders(); contents = msg.contents; folders = msg.folders; add_to_Folders(msg); return *this; }
在有些时候会用到swap操作,比如sort排序等,我们也实现一个Message版本的swap
void swap(Message &lhs, Message &rhs) { //将lhs从关联的folders中移除 lhs.remove_from_Folders(); //将rhs从关联的folders中移除 rhs.remove_from_Folders(); //交换两个成员 swap(lhs.contents, rhs.contents); swap(lhs.folders, rhs.folders); //重新将lhs添加到关联的folders中 lhs.add_to_Folders(lhs); //重新将rhs添加到关联的folders中 rhs.add_to_Folders(rhs); }
同样的道理,为实现folders的拷贝构造我单独实现一个私有函数add_msgs,将其
// 将f中的msgs添加到本folder中 void Folder::add_msgs(const Folder &f) { //将f的msgs添加到本folder for (auto mp : f.msgs) { //将消息保存在本folder mp.second->save(*this); } } Folder::Folder(const Folder &f) { this->msgs = f.msgs; this->name = f.name; add_msgs(f); }
实现folders的析构函数,同样我也实现了一个私有函数remove_msgs
//删除folder中的所有msgs void Folder::remove_msgs() { for (auto msgit = this->msgs.begin(); msgit != this->msgs.end();) { msgit->second->folders.erase(this); msgit = this->msgs.erase(msgit); } } //析构函数 Folder::~Folder() { remove_msgs(); msgs.clear(); }
同样道理拷贝赋值运算符的重载逻辑是拷贝构造和析构的综合
//拷贝赋值运算符 Folder &Folder::operator=(const Folder &f) { // 先从本folder的msg解除和本folder的关联 remove_msgs(); //再将f的参数赋值给本folder this->name = f.name; this->msgs = f.msgs; //将msgs添加到本folder add_msgs(f); }
同样我们实现Folder的swap函数
void swap(Folder &lf, Folder &rf) { //先解除lf的msg和其的联系 lf.remove_msgs(); //再解除rf的msg和其的联系 rf.remove_msgs(); //交换数据结构 swap(lf.msgs, rf.msgs); swap(lf.name, rf.name); //最后各自绑定msg和其folder的联系 lf.add_msgs(lf); rf.add_msgs(rf); }
接下来我们写一个函数测试上述赋值,析构,构造是否存在问题
void test_msgfolder() { auto f1 = new Folder("folder1"); auto msg1 = new Message("msg1"); msg1->save(*f1); auto f2 = new Folder("folder2"); auto msg2 = new Message("msg2"); msg2->save(*f2); auto f3 = new Folder(*f1); *f3 = *f2; swap(f1, f2); delete (f1); delete (msg1); delete (f2); delete (msg2); delete (f3); }
上面的测试函数,创建了三个Folder对象和两个Message对象,调用了对象的拷贝构造,拷贝赋值以及析构等操作,在主函数中调用测试函数,程序运行稳定。
总结
本文主要通过Folder和Message的示例,演示了拷贝构造和拷贝赋值的控制逻辑,在两个类互相引用的情况下如何保证代码高效稳定运行。
源码链接
https://gitee.com/secondtonone1/cpplearn
想系统学习更多C++知识,可点击下方链接。
C++基础
这篇关于C++类的拷贝控制demo的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享