练习13.22 假定我们希望HasPtr的行为像一个值。即,对于对象所指向的string成员,每个对象都有一份自己的拷贝。我们将在下一节介绍拷贝控制成员的定义。但是,你已经学习了定义这些成员所需的所有知识。在继续学习下一节之前,为HasPtr编写拷贝构造函数和拷贝赋值运算符。
class HasPtr {
public:
HasPtr() = default;
HasPtr(const std::string &s) :ps(new std::string(s)), i(0) {}
HasPtr(const HasPtr& m) :ps(new std::string(*m.ps)), i(m.i) {}
HasPtr& operator=(const HasPtr& rhs)
{
ps = new std::string(*rhs.ps);
i = (rhs.i);
return *this;
}
private:
std::string *ps;
int i;
};
练习13.23 比较上一节练习中你编写的拷贝控制成员和这一节中的代码。确定你理解了你的代码和我们的代码之间的差异(如果有的话)。
拷贝赋值运算符要考虑到自赋值的情况。
#include <iostream>
#include <string>
using namespace std;
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) :ps(new std::string(s)), i(0) {}
HasPtr(const HasPtr &m) :ps(new std::string(*m.ps)), i(m.i) {}
HasPtr& operator=(const HasPtr &rhs)
{
auto newp = new std::string(*rhs.ps); //new动态分配
delete ps;
ps = newp;
i = rhs.i;
return *this;
}
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
};
练习13.24 如果本节中的HasPtr版本未定义析构函数,将会发生什么?如果未定义拷贝构造函数,将会发生什么?
如果未定义析构函数,会合成析构函数,将不会删除ps分配的动态内存,造成内存泄漏。
如果为定义拷贝构造函数,会合成拷贝构造函数。当两个对象的指针简单拷贝,会造成多个HasPtr可能指向同一个内存,我们对相同内存使用两次delete结果将是未定义的。
练习13.25假定希望定义StbBlob类值版本,而且希望继续使用shared_ptr,这样我们的StrBlobPtr类就仍能指向vector的weak_ptr了。你修改后的类将需要一个拷贝构造函数和一个拷贝赋值运算符,但不需要析构函数。解释拷贝构造函数需要做什么。解释为什么不需要析构函数。
拷贝构造函数只需简单的拷贝数据成员,因为成员data是智能指针,无需担心像HasPtr那样造成内存泄漏。
智能指针根据实际情况自动释放内存。
练习13.26 对上一题中描述的StrBlob类,编写你自己的版本。
class StrBlob
{
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob() :data(std::make_shared<std::vector<std::string>>()) {}
StrBlob(std::initializer_list<std::string> il) :data(std::make_shared<std::vector<std::string>>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
std::string& front() { front_check(); return data->front(); }
std::string& back() { back_check(); return data->back(); }
const std::string& front() const { front_check(); return data->front(); }
const std::string& back()const { back_check(); return data->back(); }
//拷贝构造函数
StrBlob(const StrBlob &m) :data(m.data) {}
//赋值运算符
StrBlob& operator=(const StrBlob &rhs) { data = rhs.data; return *this; }
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_t, const std::string &) const;
void front_check() const;
void back_check() const;
};
void StrBlob::check(size_t i, const std::string& msg) const
{
if (i >= data->size()) {
throw std::out_of_range(msg);
}
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
void StrBlob::front_check() const
{
check(0, "front_back on empty StrBlob");
}
void StrBlob::back_check() const
{
check(0, "back_chcek on empty StrBlob");
}
练习13.27定义你自己使用的引用计数版本的HasPtr。
#include <string>
#include <memory>
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) :ps(new std::string(s)), i(0), use(new std::size_t(1)) {};
HasPtr(const HasPtr&m) :ps(m.ps), i(m.i), use(m.use) { ++*use; }
HasPtr& operator=(const HasPtr&);
~HasPtr();
private:
std::string *ps;
int i;
std::size_t *use;
};
HasPtr& 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::~HasPtr()
{
if (--*use == 0) {
delete ps;
delete use;
}
}
练习13.28 给定下面的类,为其实现一个默认构造函数和必要的拷贝控制成员。
(a)
class TreeNode {
private:
std::string value;
int count;
TreeNode *left;
TreeNode *right;
};
(b)
class BinStrTree{
private:
TreeNode *root;
};
//a
class TreeNode {
public:
TreeNode(const std::string &s, int i) :value(s), count(1), left(nullptr), right(nullptr) {}
TreeNode(const TreeNode &m) :value(m.value), count(m.count), left(m.left), right(m.right) {}
TreeNode& operator=(const TreeNode &rhs)
{
if (value == rhs.value) {
++count;
}
return *this;
}
private:
std::string value;
int count;
TreeNode *left;
TreeNode *right;
};
//b
class BinStrTree{
public:
BinStrTree():root(nullptr){}
BinStrTree(const BinStrTree &m) :root(m.root) {}
private:
TreeNode *root;
};