c++primer练习13.22-13.38
练习13.22
假定我们希望HasPtr的行为像一个值。即,对于对象所指向的string成员,每个对象都有一份自己的拷贝。编写拷贝构造函数,拷贝赋值函数
class HasPtr{ public:: HasPtr(HasPtr&b):ptr(new std::string(*b.ptr)),cur(b.cur){ } HasPtr &operator=(const HasPtr&a){delete ptr;ptr=new std::string(*a.ptr);cur=a.cur;} private:: std::string*ptr; int cur; };
练习13.23
理解上一题和本节代码之间的差异
::上一题代码不够全面,没有设计析构函数,由于new的ptr不能默认删除,得在析构函数里delete,并且拷贝赋值函数应该用临时值拷贝,防止自赋值的未定义行为,类值的行为表明每一个对象都拥有自己独一份的拷贝
练习13.24
如果本节未定义析构函数,会发生什么?如果为定义构造函数,将会发生什么?
::未定义析构函数,动态内存将不会回收,未定义构造函数,析构函数的delete将引起错误,删除非动态内存
练习13.25
假定希望定义StrBlob的类值版本,而且希望继续使用shared_ptr,这样我们的StrBlob类就仍能使用指向vector的weak_ptr了。你修改后的类将需要一个拷贝构造函数和一个拷贝赋值运算符,但不需要析构函数。解释为什么需要拷贝构造和赋值运算符,和为什么不用析构
::为了达到类值的效果,shared_ptr必须是值初始化的,而不是拷贝初始化,拥有独一份的值,赋值同理,shared_ptr指针可以自己管理内存,无需手动delete,默认析构即可
练习13.26
编写StrBlob类
class StrBlob{ friend class StrBlobPtr; public: StrBlob(StrBlob&a):data(make_shared<vector<string>>(*a.data); ) StrBlob &operator=(const StrBlob&a){auto d=a.data;data=d;return *this; } private: std::shared_ptr<std::vector<std::string> > data; void check(size_type i, const std::string &msg) const; };
练习13.27
定义你自己的使用引用计数版本的HasPtr
class HasPtr{ public: HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){ } HasPtr(HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use; } HasPtr &operator=(const HasPtr&a); ~HasPtr(); private: std::string*ptr; int i; size_t*use; }; HasPtr& HasPtr::operator=(const HasPtr&a) { ++*a.use; if(--use==0) {delete ptr; delete use; } ptr=a.ptr; use=a.use; i=a.i; return *this; } HasPtr::~HasPtr() { if(--*use==0) { delete ptr; delete use; } }
练习13.28
给定下面的类,为其实现一个默认构造函数和必要的拷贝控制成员
class TreeNode { public: TreeNode():value(string()),count(1),left(new TreeNode),right(new TreeNode){ } TreeNode(const string&s):value(s),count(1),left(new TreeNode),right(new TreeNode){ } TreeNode (const TreeNode a):value(a.value),count(a.count),left(a.left),right(a.right){ } TreeNode &operator=(const TreeNode&a); ~TreeNode(); private: std::string value; int count; TreeNode *left; TreeNode *right; }; TreeNode &TreeNode::operator=(const TreeNode&a) { value(a.value); count(a.count); left(a.left); right(a.right); return *this; }
class BinStrTree{ friend class TreeNode; public: BinStrTree():root(new TreeNode){ } BinStrTree(const TreeNode&a):root(&a){ } BinStrTree(const BinStrTree&a):root(a.root){++a->count; } BinStrTree &operator=(const BinStrTree&a); ~BinStrTree(); private: TreeNode*root; }; BinStrTree&operator=(const BinStrTree&a) { ++a->count; if(--*this.count==0) { delete root; } root=a; } BinStrTree::~BinStrTree() { if(--*this.count==0) { delete root; } }
练习13.29
解释(HasPtr&,HasPtr&)中对swap的调用不会导致递归循环
::参数可以区别开标准库和类swap
练习13.30
为你的类值版本的HasPtr编写swap函数,并测试它。为你的swap函数添加打印语句,指出函数什么时候会执行
#include <string> #include <iostream> #include <algorithm> using std::string; class HasPtr{ friend void swap(HasPtr&,HasPtr&); public: HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){ } HasPtr(HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use; } HasPtr &operator=(const HasPtr&a); ~HasPtr(); string* called(){return ptr;} private: std::string*ptr; int i; size_t*use; }; HasPtr& HasPtr::operator=(const HasPtr&a) { ++*a.use; if(--use==0) {delete ptr; delete use; } ptr=a.ptr; use=a.use; i=a.i; return *this; } HasPtr::~HasPtr() { if(--*use==0) { delete ptr; delete use; } } inline void swap(HasPtr &a,HasPtr &b) { using std::swap; swap(a.ptr,b.ptr); swap(a.i,b.i); std::cout<<"done"<<std::endl; } int main() { HasPtr a("beauty"),b("day"); swap(a,b); std::cout<<*a.called()<<std::endl; }
函数只在调用交换HasPtr类对象时执行,函数内部的仍是标准库
练习13.31
为你的HasPtr类定义一个<运算符,并定义一个HasPtr的vector,为这个vector添加一些元素,并对它执行sort。注意何时会调用swap。
#include <string> #include <iostream> #include <algorithm> #include <vector> using std::string; class HasPtr{ friend void swap(HasPtr&,HasPtr&); friend bool operator<(const HasPtr&a,const HasPtr &b); public: HasPtr(const string&s ):ptr(new string(s)),i(0),use(new size_t(1)){ } HasPtr(const HasPtr&b):ptr(b.ptr),i(b.i),use(b.use){++*use; } HasPtr &operator=(const HasPtr&a); ~HasPtr(); string* called(){return ptr;} // bool operator<(HasPtr &a); private: std::string*ptr; int i; size_t*use; }; HasPtr& HasPtr::operator=(const HasPtr&a) { ++*a.use; if(--use==0) {delete ptr; delete use; } ptr=a.ptr; use=a.use; i=a.i; return *this; } bool operator<(const HasPtr&a,const HasPtr &b) { std::cout<<"do<"<<std::endl; return *a.ptr<*b.ptr; } HasPtr::~HasPtr() { if(--*use==0) { delete ptr; delete use; } } inline void swap(HasPtr &a,HasPtr &b) { using std::swap; swap(a.ptr,b.ptr); swap(a.i,b.i); std::cout<<"done"<<std::endl; } int main() { HasPtr a("beauty"),b("day"),c("oh"); std::vector<HasPtr> line{a,b,c}; std::sort(line.begin(),line.end()); std::cout<<*line.begin()->called()<<std::endl; }
元素多的时候会用自己定义的swap
练习13.32
类指针的HasPtr版本会从swap函数收益吗?如果会,得到了什么益处,如果不会,为什么
::不会收益,默认的swap就能交换指向,无需多此一举
练习13.33
为什么Message的成员save和remove的参数是一个Folder&?为什么我们不将参数定义为Folder或者const Folder&
::为了包含该message对象的Folder能删除或添加该指针
练习13.34
编写本节所描述的Message
class Message{ friend class Folder; public: Message(const string&s=" "):content(s){ } Message(const Message&a):content(a.content),folders(a.folders){add_to_Folders(a); } Message& operator=(const Message &a){remove_from_Folders();content=a.content;folders=a.folders; add_to_Folders(a);return *this; } void save(Folder&f) { folders.insert(&f); f.addMsg(this); } void remove(Folder&f) { folders.erase(&f); f.remMsg(this); } private: string content; set<Folder*> folders; void add_to_Folders(const Message &m) { for(auto d:m.folders) d->addMsg(this); } void remove_from_Folders() { for(auto d:folders) d->remMsg(this); } };
练习13.35
如果Message使用合成的拷贝控制成员,将会发生什么?
::Message的folders和Folder的Msg就没有意义了
练习13.36
设计并实现对应的Folder类。此类应该保存一个指向Folder中包含的Message的set
class Folder{ friend class Message; public: void addMsg(Message*a){Msg.insert(a);} void remMsg(Message*a){Msg.erase(a); } private: set<Message*> Msg; };
练习13.37
为Message添加成员,实现向folders添加删除
::看13.34
练习13.38
我们并未使用拷贝并交换方式来设计Message的赋值运算符,你认为其原因是什么?
::没有什么益处
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具