Chapter 13: Copy Control

Exercises Section 13.1.1

Ex13.1

copy constructor:第一个参数为类类型的引用,任何其他参数都有默认值。

在如下情况下使用拷贝构造函数:

  1. 用 = 定义变量
  2. 传递一个对象作为参数给非引用类型
  3. 函数的返回对象的类型为非引用返回类型
  4. 用括号初始化数组元素或聚合类的成员

Ex13.2

如下传入的参数类型为非引用,调用此拷贝构造函数时则要求拷贝实参,而拷贝实参又要求调用拷贝构造函数,会陷入无限死循环

Sales_data::Sales_data(Sales_data rhs);

Ex13.3

拷贝 StrBlob 和 StrBlobPtr 时,都调用合成的拷贝构造函数。对 StrBlob 来说,拷贝其成员 data,使用 shared_ptr 的拷贝构造函数进行拷贝,其引用计数加 1;对 StrBlobPtr 来说,拷贝其成员 wptr 时,使用 weak_ptr 的拷贝构造函数进行拷贝,引用计数不变;拷贝其 curr 成员时,属于内置类型,直接拷贝。

Ex13.4

// Point 是一个有 public 拷贝构造函数的类
Point global;
Point foo_bar(Point arg)
{
	Point local = arg, *heap = new Point(global);
	*heap = local;
	Point pa[4] = {local, *heap};
	return *heap;

上述代码使用拷贝构造函数的地方有:

  1. Point local = arg
  2. *heap = local
  3. Point pa[4] = {local, *heap}
  4. return *heap;

Ex13.5

HasPtr::HasPtr(const HasPtr &hp): ps(new string(*hp.ps)), i(hp.i) { }

Exercises Section 13.1.2

Ex13.6

拷贝赋值运算符:可以看作一个名为operator=的成员函数,接受一个与其所在类相同类型的参数,返回一个指向其左侧运算对象的引用。

何时使用:对类对象进行赋值操作时使用;

进行的操作:将右侧运算对象的每个非 static 成员赋予左侧运算对象的对应成员; 当一个类未定义自己的拷贝赋值运算符时,编译器会生成合成拷贝运算符。

Ex13.7

对于 StrBlob 类,用 shared_ptr 的拷贝赋值运算符对 data 成员进行赋值(引用计数加1);

对于 StrBlobPtr 类,用 weak_ptr 的拷贝赋值运算符对 wptr 成员赋值(引用计数不变),对curr成员直接赋值。

Ex13.8

HasPtr &HasPttr::operator=(const HasPtr &hp)
{
	auto tmp = 
	i = hp.i;
	return *this;
}

Exercises Section 13.1.3

Ex13.9

destructor:与所在类的名字一样,带有 ~ 前缀的类成员函数,没有返回值和参数。

析构函数用于释放类所占内存,类成员销毁顺序和类成员初始化顺序相反。

合成析构函数先执行函数体然后销毁成员。

当类没有定义自己的析构函数时,编译器就会定义合成析构函数。

Ex13.10

StrBlob:执行合成析构函数,先执行析构函数空函数体,然后再销毁成员 data,销毁 data 时调用 shared_ptr 的析构函数,将引用计数减 1,如果引用计数变为 0 则 销毁共享的 vector 对象。

StrBlobPtr:执行合成析构函数,先执行析构函数空函数体,然后销毁成员 curr 、wptr;curr 为内置类型直接销毁,销毁 wptr 时会调用 weak_ptr 的析构函数,引用计数不变。

Ex13.11

~HasPtr()
{
	delete ps;
}

Ex13.12

// 给出析构函数调用次数
bool fcn(const Sales_data *trans, Sales_data accum)
{
	Sales_data item1(*trans), item2(accum);
	return item1.isbn() != item2.isbn();
}

accum 为传值,故而函数 fcn 结束则调用一次析构函数;item1 和 item2 为临时类变量,故而函数 fcn 结束则分别调用一次析构函数;共计调用三次析构函数

Ex13.13

struct X
{
	X() { std::cout << "X()" << std::endl; }
	X(const X &) { std::cout << "X(const X &)" << std::endl; }
	X & operator=(const X &) { std::cout << "operator=(const X &)" << std::endl; }
	~X() { std::cout << "~X()" << std::endl; }
};

Exercises Section 13.1.4

Ex13.14

使用合成的拷贝控制成员,故而f(a)f(b)f(c)输出相同的内容。

Ex13.15

会改变输出结果,b = a、c = b 及三次调用 f() 都会调用拷贝构造函数产生新的序列号,故而 f(a)f(b)f(c)值都不相同且不与 a、b、c 中的 mysn 相同。

Ex13.16

会改变输出结果,b = a c = b会调用拷贝函数产生新的序列号,故而f(a)f(b)f(c)中值都不相同,但对应与 a、b、c 中的 mysn 值相同。

Ex13.17

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Exercises Section 13.1.6

Ex13.18

#include<iostream>

using namespace std;

class Employee
{
	friend void get_info(const Employee &);
public:
	Employee() { id = num++; }
	Employee(const string &s): name(s) { id = num++; }
	Employee(const Employee &e) 
	{
		name = e.name;
		id = num++;
	}
	Employee & operator=(Employee &rhs)
	{
		name = rhs.name;
		return *this;
	}
private:
	static unsigned num;
	string name;
	unsigned id;
};

unsigned Employee::num = 0;

void get_info(const Employee &e)
{
	cout << e.name << "-" << e.id << endl; 
}

int main()
{
	Employee e1("Mike");
	Employee e2 = e1;	// 拷贝构造
	Employee e3;	// 默认构造

	e3 = e1;
	get_info(e1);
	get_info(e2);
	get_info(e3);

	system("pause");
	return 0;
}

Ex13.19

需要定义自己的拷贝控制成员,如果使用合成的拷贝构造函数则会产生重复的 id。

Ex13.20

类 TextQuery 有两个成员:

  1. shared_ptr<vector> file
  2. map<string, shared_ptr<set<line_no>>> wm;

拷贝TextQuery:拷贝 file 会调用 shared_ptr 的拷贝构造函数,引用计数加 1,然后调用 vector 接着调用 string 的拷贝构造函数;拷贝 wm,调用 map 的构造函数然后调用 string 和 shared_ptr 的拷贝构造函数。

赋值 TextQuery:赋值 file,file 原先指向的对象的引用计数减 1;赋值 wm 与拷贝 wm 类似。

销毁 TextQuery:销毁 wm ,调用 map 的析构函数,然后调用 string 和 shared_ptr 的析构函数;销毁 file,调用 shared_ptr 的析构函数,引用计数减 1,如果引用计数变为 0 则销毁 vector 中元素。

类 QueryResult 有三个成员:

  1. string sought;
  2. shared_ptr<set<line_no>> p
  3. shared_ptr<vector> line

拷贝 QueryResult:直接拷贝 sought;拷贝 p 会调用 shared_ptr 的拷贝构造函数,引用计数加 1,然后调用 set 的拷贝构造函数;拷贝 line 会调用 shared_ptr 的拷贝构造函数,引用计数加 1,然后调用 vector 接着调用 string 的拷贝构造函数。

赋值 QueryResult:直接赋值 sought;赋值 p 会使 p 原先指向的对象的引用计数减 1;赋值 line 会使 lie 原先指向的对象的引用计数减 1。

销毁 QueryResult:销毁 line 会调用 shared_ptr 的析构函数,引用计数减 1,如果引用计数变为 0 则销毁 vector 中的元素;销毁 p 会调用 shared_ptr 的析构函数,引用计数减 1,如果引用计数变为 0 则销毁 set 中的元素;直接销毁 sought。

Ex13.21

可以不定义自己的拷贝控制成员,类的各个成员都能够正确执行拷贝赋值和销毁操作,所以可使用合成的拷贝控制成员实现正确的功能。

Exercises Section 13.22

Ex13.22

HasPtr(const std::string &s = std::string()): ps(new std::string()), i(0) { }
HasPtr(const HasPtr &hp): ps(new std:string(*hp.ps)), i(hp.i) { }
HasPtr & operator=(const HasPtr &);

Exercises Section 13.2.1

Ex13.24

如果没有定义析构函数,可能会造成内存泄露,因为 ps 通过 new 在堆上分配内存,需要使用 delete 释放内存。

Ex13.25

拷贝构造函数和拷贝赋值运算符需要做的就是分配一个新的内存并赋予其值然后进行拷贝,不需要析构函数是因为智能指针自己有析构函数进行内存的释放。

Ex13.26

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <algorithm>
#include <iterator>
#include <unordered_map>
#include <memory>

using namespace std;

class strBlobPtr;
class strBlob 
{
    friend void print(strBlob s);
    friend class strBlobPtr;
public:
    typedef vector<string>::size_type size_type;
    strBlob();
    strBlob(initializer_list<string> il);
    strBlob(const strBlob& str) : data(make_shared<vector<string>>(*str.data)) {}        //拷贝构造函数
    strBlob &operator=(const strBlob &str);                                                //拷贝赋值运算符
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    void push_back(const string &t);
    void pop_back();
    const string &front();
    const string &back();
    strBlobPtr begin();
    strBlobPtr end();
private:
    shared_ptr<vector<string>> data;
    void check(size_type i, const string &msg) const;
};

class strBlobPtr 
{
public:
    strBlobPtr() : curr(0) {}
    strBlobPtr(strBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
    string & deref() const;
    strBlobPtr &incr();
private:
    shared_ptr<vector<string>> check(size_t t, const string &str) const;
    weak_ptr<vector<string>> wptr;
    size_t curr;
};

void print(strBlob s);

int main()
{
    strBlob p1({ "asd", "qew", "jkl" });
    print(p1);
    p1.push_back("dqw");
    print(p1);
    p1.pop_back();
    p1.pop_back();
    print(p1);
    cout << endl;
    cout << p1.front() << endl;
    cout << p1.back() << endl;
    system("pause");
    return 0;
}

strBlob::strBlob() : data(make_shared<vector<string>>()) {}
strBlob::strBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {}

strBlob & strBlob::operator=(const strBlob & str)
{
    auto str1 = make_shared<vector<string>>(*str.data);
    data = str1;
    return *this;
    // TODO: 在此处插入 return 语句
}

void strBlob::push_back(const string & t)
{
    data->push_back(t);
}

void strBlob::pop_back()
{
    check(0, "pop_back on empty StrBlob");
    data->pop_back();
}

const string & strBlob::front()
{
    check(0, "front on empty StrBlob");
    return data->front();
    // TODO: 在此处插入 return 语句
}

const string & strBlob::back()
{
    check(0, "back on empty StrBlob");
    return data->back();
    // TODO: 在此处插入 return 语句
}

strBlobPtr strBlob::begin()
{
    return strBlobPtr(*this);
}

strBlobPtr strBlob::end()
{
    return strBlobPtr(*this, data->size());
}


void strBlob::check(size_type i, const string & msg) const
{
    if (i >= data->size())
        throw out_of_range(msg);
}

void print(strBlob s)
{
    for (auto c : *(s.data))
        cout << c << endl;
    cout << endl;
}

string & strBlobPtr::deref() const
{
    auto p = check(curr, "dereference past end");
    return (*p)[curr];
    // TODO: 在此处插入 return 语句
}

strBlobPtr & strBlobPtr::incr()
{
    check(curr, "increment past end of strBlobPtr");
    ++curr;
    return *this;
    // TODO: 在此处插入 return 语句
}

shared_ptr<vector<string>> strBlobPtr::check(size_t t, const string & str) const
{
    auto ret = wptr.lock();
    if (!ret)
        throw runtime_error("unbound strBlobPtr");
    if (t >= ret->size())
        throw out_of_range(str);
    return ret;
}

Exercises Section 13.2.2

Ex13.27

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 &p): ps(p.ps), i(p.i), use(p.use) { ++*use; }
	HasPtr & operator=(const HasPtr &);
	~HasPtr();
private:
	std::string *ps;
	int i;
	std::size_t *use;
}HasPtr::~HasPtr()
{
	if (--*use == 0)
	{
		delete ps;
		delete 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;
}

Ex13.28

class TreeNode
{
public:
	TreeNode(): value(""), count(1), left(nullptr), right(nullptr) {}
	TreeNode(std::string s, TreeNode *lchild = nullptr, TreeNode *rchild = nullptr):
		value(s), count(1), left(lchild), right(rchild) {}
	TreeNode(const TreeNode &): value(tn.value), count(1), left(tn.left), right(tn.right) {}
	~TreeNode();
private:
	std::string value;
	int count;
	TreeNode *left;
	TreeNode *right;
}class BinStrTree
{
public:
	BinStrTree(): root(nullptr) {}
	BinStrTree(TreeNode *t = nullptr): root(t) {}
	BinStrTree(const BinStrTree &bst): root(bst.root) {}
	~BinStrTree();
private:
	TreeNode *root;
}

Exercises Section 13.3

Ex13.29

在函数 swap(HasPtr &, HasPtr &) 里的 swap 函数为 std 里的函数。

Ex13.30

#include<iostream>
#include<string>

using namespace std;

class HasPtr
{
	friend void swap(HasPtr &, 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 &p): ps(p.ps), i(p.i), use(p.use) { ++*use; }
	HasPtr & operator=(const HasPtr &);
	~HasPtr();
private:
	std::string *ps;
	int i;
	std::size_t *use;
};

HasPtr::~HasPtr()
{
	if (--*use == 0)
	{
		delete ps;
		delete 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;
}

inline void swap(HasPtr &lhs, HasPtr &rhs)
{
	using std::swap;
	swap(lhs.ps, rhs.ps);
	cout << "swap ps" << endl;
	swap(lhs.i, rhs.i);
	cout << "swap i" << endl;
}

int main()
{
	HasPtr hp1("hello");
	HasPtr hp2("world");
	swap(hp1, hp2);
	system("pause");
	return 0;
}

Ex13.31

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;

class HasPtr
{
	friend void swap(HasPtr &, HasPtr &);
	friend ostream &print(ostream &os, HasPtr &hp);
	friend bool operator<(HasPtr &lhs, HasPtr &rhs);
	friend string &out(HasPtr &hp);
public:
	HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0), use(new std::size_t(1)) {}
	HasPtr(const HasPtr &p): ps(p.ps), i(p.i), use(p.use) { ++*use; }
	HasPtr & operator=(const HasPtr &);
	~HasPtr();
private:
	std::string *ps;
	int i;
	std::size_t *use;
};

HasPtr::~HasPtr()
{
	if (--*use == 0)
	{
		delete ps;
		delete 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;
}

inline void swap(HasPtr &lhs, HasPtr &rhs)
{
	using std::swap;
	swap(lhs.ps, rhs.ps);
	cout << "swap ps" << endl;
	swap(lhs.i, rhs.i);
	cout << "swap i" << endl;
}

string &out(HasPtr &hp)
{
	return *hp.ps;
}

bool operator<(HasPtr &lhs, HasPtr &rhs)
{
	return (*lhs.ps).size() < (*rhs.ps).size();
}

ostream &print(ostream &os, HasPtr &hp)
{
	os << *hp.ps << " " << hp.i << endl;
	return os;
}

int main()
{
	vector<HasPtr> vhp;
	string word;
	while (cin >> word)
		vhp.push_back(*new HasPtr(word));
	sort(vhp.begin(), vhp.end());
	for (auto &c : vhp)
		cout << out(c) << endl;
	system("pause");
	return 0;
}

Ex13.32

相比与类值版本的 swap 函数,指针版本的 swap 函数交换没有明显的优势,因为指针其实就是一个变量,只不过存储了地址而已。

Exercises Section 13.4

Ex13.33

如果定义为 Folder,那么修改的只是传入对象的副本的值;如果定义为 const Folder &,那么就修改其内容。

Ex13.34

class Message 
{
	friend class Folder;
	friend void swap(Message &, Message &);
public:
	explicit Message(const std::string &str = ""): contents(str) {}
	Message(const Message &);
	Message & operator=(const Message &);
	~Message();
	void save(Folder &);
	void remove(Folder &);
private:
	std::string contents;
	std::set<Folder*> folders;
	void add_to_Folders(const Message &);
	void remove_from_Folders();
};

void Message::save(Folder &f)
{
	folders.insert(&f);
	f.addMsg(this);
}

void Message::remove(Folder &f)
{
	folders.erase(&f);
	f.remMsg(this);
}

void Message::add_to_Folders(const Message &m)
{
	for (auto f : m.folders)
		f->addMsg(this);
}

Message::Message(const Message &m): contents(m.contents), folders(m.folders)
{
	add_to_Folders(m);
}

void Message::remove_from_Folders()
{
	for (auto f : folders)
		f->remMsg(this);
}

Message::~Message()
{
	remove_from_Folders();
}

Message &Message::operator=(const Message &rhs)
{
	remove_from_Folders();
	contents = rhs.contents;
	folders = rhs.folders;
	add_to_Folders(rhs);
	return *this;
}

void swap(Message &lhs, Message &rhs)
{
	using std::swap();
	for (auto f : lhs.folders)
		f->remMsg(&lhs);
	for (auto f : rhs.folders)
		f->remMsg(&rhs);
	swap(lhs.folders, rhs.folders);
	sawp(lhs.contents, rhs.contents);
	for (auto f : lhs.folders)
		f->remMsg(&lhs);
	for (auto f : rhs.folders)
		f->remMsg(&rhs);
}

Ex13.35

使用合成的拷贝控制则无法将 Message 添加到 Folder 中。

Ex13.36

class Folder
{
	friend void swap(Message &, Message &);
	friend class Message;
public:
	Folder() = default;
	Folder(const Folder &);
	Folder & operator=(const Folder &);
	~Folder();
	void save(Message &);
	void remove(Message &);
	void print();
private:
	set<Message*> messages;
	void add_to_message(const Folder &);
	void remove_from_message();
	void addMsg(Message*);
	void remMsg(Message*);
};

void Folder::save(Message &m)
{
	messages.insert(&m);
	m.add_to_Folders(this);
}

void Folder::remove(Message &m)
{
	messages.erase(&m);
	m.remove_from_Folders(this);
}

void Folder::addMsg(Message *m)
{
	messages.insert(m);
}

void Folder::remMsg(Message *m)
{
	messages.erase(m);
}

void Folder::add_to_message(const Folder &f)
{
	for (auto m : f.messages)
		m->add_to_Folders(this);
}

void Folder::remove_from_message()
{
	for (auto m : messages)
		m->remove_from_Folders(this);
}

Folder::Folder(const Folder &f): messages(f.messages)
{
	add_to_message(f);
}

Folder & Folder::operator=(const Folder &f)
{
	remove_from_message();
	messages = f.messages;
	add_to_message(f);
	return *this;
}

Folder::~Folder()
{
	remove_from_message();
}

void Folder::print()
{
	cerr << "Folder contains " << messages.size() << " messages" << endl;
	int ctr = 1;
	for (auto m : messages)
		cerr << "Message " << ctr++ << ":\n\t" << m->contents << endl; 
}

Ex13.37

class Message 
{
	// 其余成员函数照旧
public:
	void addFolder(Folder*);
	void remFolder(Folder*);
};

void Message::addFolder(Folder *f)
{
	folders.insert(f);
}

void Message::remFolder(Folder *f)
{
	folders.erase(f);
}

Ex13.38

使用 swap 功能没问题,但是对于 rhs 而言,创建和销毁往返两次是没有必要的,故而不使用拷贝和 swap 函数实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值