C++ Primer第五版_第十三章习题答案(21~30)
嘿,兄弟姐妹们!今天咱们来聊聊C++里的拷贝控制成员,这玩意儿听着高大上,其实说白了就是教你如何在对象复制、赋值时优雅地管理内存,避免搞出内存泄漏或者指针混乱的幺蛾子。先拿TextQuery和QueryResult这两哥们儿来说事儿,它们的合成版本已经够用了,咱们就不必再画蛇添足了。但HasPtr这家伙可就不一样了,它要求每个对象都要有自己的string副本,所以咱们还得给它量身定制拷贝构造函数和拷贝赋值运算符。
说到拷贝控制,你可能会问,如果不定义析构函数和拷贝构造函数,会发生啥?简单,内存泄漏和指针共享呗,这可不是闹着玩的。再来说说StrBlob,这货比HasPtr高级,用了智能指针shared_ptr,这样咱们就不用操心析构函数了,引用计数为0时它会自动释放内存,多省心!
最后来聊聊HasPtr的swap函数,这可是个避免递归循环的聪明设计。通过参数类型的不同,swap函数不会自己调用自己,而是巧妙地交换两个对象的内部数据,简洁高效!看到没,C++的世界里,每个细节都藏着智慧,咱们得多学多用,才能玩转这门语言!
文章目录
-
-
- 练习13.21
- 练习13.22
- 练习13.23
- 练习13.24
- 练习13.25
- 练习13.26
- 练习13.27
- 练习13.28
- 练习13.29
- 练习13.30
-
练习13.21
你认为 TextQuery 和 QueryResult 类需要定义它们自己版本的拷贝控制成员吗?如果需要,为什么?实现你认为这两个类需要的拷贝控制操作。
合成的版本满足所有的需求。因此不需要自定义拷贝控制成员。
练习13.22
假定我们希望 HasPtr 的行为像一个值。即,对于对象所指向的 string 成员,每个对象都有一份自己的拷贝。我们将在下一节介绍拷贝控制成员的定义。但是,你已经学习了定义这些成员所需的所有知识。在继续学习下一节之前,为 HasPtr 编写拷贝构造函数和拷贝赋值运算符。
#ifndef ex13_22_h
#define ex13_22_h#include <string>
class HasPtr
{
public:HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {}HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) {}HasPtr& operator=(const HasPtr &hp){auto new_p = new std::string(*hp.ps);delete ps;ps = new_p;i = hp.i;return *this;}~HasPtr(){delete ps;}
private:std::string *ps;int i;
};#endif // !ex13_22_h
练习13.23
比较上一节练习中你编写的拷贝控制成员和这一节中的代码。确定你理解了你的代码和我们的代码之间的差异。
练习13.24
如果本节的 HasPtr 版本未定义析构函数,将会发生什么?如果未定义拷贝构造函数,将会发生什么?
如果未定义析构函数,将会发生内存泄漏。如果未定义拷贝构造函数,将会拷贝指针的值,指向同一个地址。
练习13.25
假定希望定义 StrBlob 的类值版本,而且希望继续使用 shared_ptr,这样我们的 StrBlobPtr 类就仍能使用指向vector的 weak_ptr 了。你修改后的类将需要一个拷贝的构造函数和一个拷贝赋值运算符,但不需要析构函数。解释拷贝构造函数和拷贝赋值运算符必须要做什么。解释为什么不需要析构函数。
拷贝构造函数和拷贝赋值运算符要重新动态分配内存。因为 StrBlob 使用的是智能指针,当引用计数为0时会自动释放对象,因此不需要析构函数。
练习13.26
对上一题中描述的 strBlob 类,编写你自己的版本。
#ifndef ex13_26_h
#define ex13_26_h#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <exception>using std::vector; using std::string;class ConstStrBlobPtr;class StrBlob
{
public:using size_type = vector<string>::size_type;friend class ConstStrBlobPtr;ConstStrBlobPtr begin() const;ConstStrBlobPtr end() const;StrBlob() :data(std::make_shared<vector<string>>()) {}StrBlob(std::initializer_list<string> il) :data(std::make_shared<vector<string>>(il)) {}// copy constructorStrBlob(const StrBlob& sb) : data(std::make_shared<vector<string>>(*sb.data)) {}// copyassignment operatorsStrBlob& operator=(const StrBlob& sb);size_type size() const { return data->size(); }bool empty() const { return data->empty(); }void push_back(const string &t) { data->push_back(t); }void pop_back(){check(0, "pop_back on empty StrBlob");data->pop_back();}std::string& front(){check(0, "front on empty StrBlob");return data->front();}std::string& back(){check(0, "back on empty StrBlob");return data->back();}const std::string& front() const{check(0, "front on empty StrBlob");return data->front();}const std::string& back() const{check(0, "back on empty StrBlob");return data->back();}private:void check(size_type i, const string &msg) const{if (i >= data->size()) throw std::out_of_range(msg);}private:std::shared_ptr<vector<string>> data;
};class ConstStrBlobPtr
{
public:ConstStrBlobPtr() :curr(0) {}ConstStrBlobPtr(const StrBlob &a, size_t sz = 0) :wptr(a.data), curr(sz) {} // should add constbool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; }const string& deref() const{ // return value should add constauto p = check(curr, "dereference past end");return (*p)[curr];}ConstStrBlobPtr& incr(){check(curr, "increment past end of StrBlobPtr");++curr;return *this;}private:std::shared_ptr<vector<string>> check(size_t i, const string &msg) const{auto ret = wptr.lock();if (!ret) throw std::runtime_error("unbound StrBlobPtr");if (i >= ret->size()) throw std::out_of_range(msg);return ret;}std::weak_ptr<vector<string>> wptr;size_t curr;
};ConstStrBlobPtr StrBlob::begin() const // should add const
{return ConstStrBlobPtr(*this);
}
ConstStrBlobPtr StrBlob::end() const // should add const
{return ConstStrBlobPtr(*this, data->size());
}StrBlob& StrBlob::operator=(const StrBlob& sb)
{data = std::make_shared<vector<string>>(*sb.data);return *this;
}#endif // !ex13_26_h
练习13.27
定义你自己的使用引用计数版本的 HasPtr。
#ifndef ex13_27_h
#define ex13_27_h#include <string>class HasPtr
{
public:HasPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0), use(new size_t(1)) {}HasPtr(const HasPtr& hp) :ps(hp.ps), use(hp.use), i(hp.i) { ++*use; }HasPtr& operator=(const HasPtr& rhs){++*rhs.use;if (--*use == 0){delete ps;delete use;}ps = rhs.ps;i = rhs.i;use = rhs.use;return *this;}~HasPtr(){if (--*use == 0){delete ps;delete use;}}
private:std::string *ps;int i;size_t *use;
};#endif
练习13.28
给定下面的类,为其实现一个默认构造函数和必要的拷贝控制成员。
(a)
class TreeNode {
pravite:std::string value;int count;TreeNode *left;TreeNode *right;
};
(b)
class BinStrTree{
pravite:TreeNode *root;
};
#ifndef ex13_28_h
#define ex13_28_h#include <string>
using std::string;class TreeNode
{
public:TreeNode() : value(string()), count(new int(1)), left(nullptr), right(nullptr) {}TreeNode(const TreeNode &rhs) : value(rhs.value), count(rhs.count), left(rhs.left), right(rhs.right) { ++*count; }TreeNode& operator=(const TreeNode &rhs);~TreeNode(){if (--*count == 0){if (left){delete left;left = nullptr;}if (right){delete right;right = nullptr;}delete count;count = nullptr;}}private:std::string value;int *count;TreeNode *left;TreeNode *right;
};TreeNode& TreeNode::operator=(const TreeNode &rhs)
{++*rhs.count;if (--*count == 0){if (left){delete left;}if (right){delete right;}delete count;}value = rhs.value;left = rhs.left;right = rhs.right;count = rhs.count;return *this;
}class BinStrTree
{
public:BinStrTree() : root(new TreeNode()) {}BinStrTree(const BinStrTree &bst) : root(new TreeNode(*bst.root)) {}BinStrTree& operator=(const BinStrTree &bst);~BinStrTree() { delete root; }private:TreeNode *root;
};BinStrTree& BinStrTree::operator=(const BinStrTree &bst)
{TreeNode *new_root = new TreeNode(*bst.root);delete root;root = new_root;return *this;
}#endif
练习13.29
解释 swap(HasPtr&, HasPtr&)中对 swap 的调用不会导致递归循环。
这其实是3个不同的函数,参数类型不一样,所以不会导致递归循环。
练习13.30
为你的类值版本的 HasPtr 编写 swap 函数,并测试它。为你的 swap 函数添加一个打印语句,指出函数什么时候执行。
#ifndef CP5_ex13_11_h
#define CP5_ex13_11_h#include <string>
#include <iostream>class HasPtr
{
public:friend void swap(HasPtr&, HasPtr&);HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {}HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) {}HasPtr& operator=(const HasPtr &hp){auto new_p = new std::string(*hp.ps);delete ps;ps = new_p;i = hp.i;return *this;}~HasPtr(){delete ps;}void show() { std::cout << *ps << std::endl; }
private:std::string *ps;int i;
};inline
void swap(HasPtr& lhs, HasPtr& rhs)
{using std::swap;swap(lhs.ps, rhs.ps);swap(lhs.i, rhs.i);std::cout << "call swap(HasPtr& lhs, HasPtr& rhs)" << std::endl;
}#endif