第 13 章 拷贝控制

练习 13.1:

        如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数;

  • 使用=定义变量
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
  • 某些类类型还会对它们所分配的对象使用拷贝初始化。

练习 13.2:

        拷贝构造函数自己的参数必须是引用类型。如果其参数不是引用类型,则调用永远也不会成功——为了调用拷贝构造函数,我们必须拷贝他的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。

练习 13.3:

        当我们拷贝一个StrBlob时,shared_ptr成员的use_count增加一。拷贝StrBlobPrts时,weak_ptr成员的use_count不会改变。(因为use_count是shared_ptr的数量)

练习 13.4:

Point global;
Point foo_bar(Point arg) // 1  实参传递给非引用类型的形参
{
    Point local = arg, *heap = new Point(global); // 2, 3  =定义变量
    *heap = local;  // 4  =定义变量
    Point pa[ 4 ] = { local, *heap }; // 5  花括号列表初始化一个数组中的元素
    return *heap; // 6  从一个返回类型为非引用类型的函数返回一个对象
}

练习 13.5:

class HasPtr
{
public:
	HasPtr(const string& s = string()): ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp): ps(new string(*hp.ps)), i(hp.i){}
private:
	string* ps;
	int i;
};

练习 13.6:

        拷贝赋值运算符就是一个名为operator=的函数。当发生赋值时使用此操作符。对于某些类,合成拷贝赋值运算符用来禁止该类型对象的赋值。如果拷贝赋值运算符并非出于此目的,它会将右侧对象的每个非static成员赋值给左侧运算对象的对应成员。当一个类没有定义自己的拷贝赋值运算符的时候,编译器会为它生成一个合成拷贝赋值运算符。

练习 13.7:

        在这两种情况下,都会发生浅拷贝。所有的指针都指向同一个地址。use_count的变化与练习13.3相同。

练习 13.8:

class HasPtr
{
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
	HasPtr& operator=(const HasPtr& hp)
	{
		delete ps;
		ps = new string(*hp.ps);
		i = hp.i;
		return *this;
	}
private:
	string* ps;
	int i;
};

练习 13.9:

        析构函数是类的一个成员函数,名字由波浪号接类名构成。类似拷贝构造函数和拷贝赋值运算符,对于某些类,合成析构函数被用来阻止该类型对象的销毁。如果不是这种情况,合成析构函数的函数体为空。当一个类没有定义自己析构函数时,编译器会为它定义一个合成析构函数。

练习 13.10:

        当StrBlob对象销毁时,动态对象的use_count将递减。如果该动态对象没有shared_ptr,它将被释放。当StrBlobPtr对象销毁时,动态分配的对象不会被释放。

练习 13.11:

#include<iostream>
using namespace std;

class HasPtr
{
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
	HasPtr& operator=(const HasPtr& hp)
	{
		auto newp = new string(*hp.ps);
		delete ps;
		ps = newp;
		i = hp.i;
		return *this;
	}
	//HasPtr& operator=(const HasPtr& hp)
	//{
	//	delete ps;
	//	// 如果hp和*this是同一个对象,我们就将从已释放的内存中拷贝数据
	//	ps = new string(*hp.ps);
	//	i = hp.i;
	//	return *this;
	//}
	~HasPtr() { delete ps; }
private:
	string* ps;
	int i;
};

练习 13.12:

        用于item2拷贝初始化的临时对象accum,函数体中构造的两个对象item1和item2。当指向一个对象的引用或指针离开作用域时,析构函数不会执行。

练习 13.13:

#include <iostream>
#include <vector>
using namespace std;

struct X {
	X() { cout << "X()" << endl; }
	X(const X&) { cout << "X(const X&)" << endl; }
	X& operator= (const X&)
    { 
		cout << "X& operator=(const X&)" << endl; 
		return *this;
	}
	~X() { cout << "~X()" << endl; }
};

void fun1(X p) { ; }	// 非引用参数传递

void fun2(X& p) { ; }	// 引用参数传递

int main(int argc, char* argv[])
{
	cout << "默认初始化:" << endl;
	X x;	// 执行默认构造函数

	cout << "传递非引用参数:" << endl;
	fun1(x);	// 执行拷贝构造函数和析构函数

	cout << "传递引用参数:" << endl;
	fun2(x);	// 构造函数和析构函数不会执行

	cout << "动态分配对象并销毁:" << endl;
	X* p = new X;	// 执行默认构造函数
	delete p;		// 执行析构函数

	cout << "添加到容器:" << endl;
	vector<X> vx;
	vx.push_back(x);	// 执行拷贝构造函数

	cout << "拷贝初始化:" << endl;
	X y = x;			// 执行拷贝构造函数

	cout << "拷贝赋值:" << endl;
	y = x;				// 执行拷贝赋值运算符

	// 函数执行完毕,执行析构函数销毁x, y和vx中的元素
	return 0;
}

练习 13.14:

程序输出结果都是一样的,都是对象a的mysn成员。

练习 13.15:

        输出结果将会改变。因为我们不使用合成的拷贝控制成员,而是使用自己定义的。函数f传入的是对象的拷贝,所以其输出的值是临时对象的数据成员。输出将是三个不同的数字。

练习 13.16:

        输出结果将会改变。此时函数f传入的不再是对象的拷贝而是对象的引用,所以其输出的值也不是临时对象的数据成员,而是原本的数据成员。

练习 13.17:

// 练习 13.14:
#include<iostream>
using namespace std;

class numbered
{
public:
	numbered()
	{
		static int unique = 10;
		mysn = unique++;
	}
	int mysn;
};

void f(numbered s) { cout << s.mysn << endl; }

int main()
{
	numbered a, b = a, c = b;
	f(a); f(b); f(c);    // a = b = c = 10

	return 0;
}
// 练习 13.15:
#include<iostream>
using namespace std;

class numbered
{
public:
	numbered()
	{
		static int unique = 10;
		mysn = unique++;
	}
	numbered(const numbered& n) { mysn = n.mysn + 1; }
	int mysn;
};

void f(numbered s) { cout << s.mysn << endl; }

int main()
{
	numbered a, b = a, c = b;    // a = 10; b = 11; c = 12
	f(a); f(b); f(c);            // 'a' = 11; 'b' = 12; 'c' = 13

	return 0;
}
// 练习 13.16:
#include<iostream>
using namespace std;

class numbered
{
public:
	numbered()
	{
		static int unique = 10;
		mysn = unique++;
	}
	numbered(const numbered& n) { mysn = n.mysn + 1; }
	int mysn;
};

void f(const numbered& s) { cout << s.mysn << endl; }

int main()
{
	numbered a, b = a, c = b;    // a = 10; b = 11; c = 12
	f(a); f(b); f(c);            // a = 10; b = 11; c = 12

	return 0;
}

练习 13.18:

#include<iostream>
using namespace std;

class Employee
{
public:
	Employee() { m_id = s_increment++; }
	Employee(const string& name)
	{
		this->m_id = s_increment++;
		this->m_name = name;
	}

	const int id() const { return this->m_id; }

private:
	string m_name;
	int m_id;
	static int s_increment;		//static成员类内声明类外定义,在类外定义时不加static
};

int Employee::s_increment = 0;

练习 13.19:

        不需要,因为在逻辑上不合理。员工不能复制在现实世界中,所以应该把拷贝控制成员定义为删除的。

#include<iostream>
using namespace std;

class Employee
{
public:
	Employee() { m_id = s_increment++; }
	Employee(const string& name)
	{
		this->m_id = s_increment++;
		this->m_name = name;
	}
	Employee(const Employee&) = delete;
	Employee& operator=(const Employee&) = delete;

	const int id() const { return this->m_id; }

private:
	string m_name;
	int m_id;
	static int s_increment;
};

int Employee::s_increment = 0;

int main()
{
	Employee e;
	cout << e.id() << endl;
}

练习 13.20:

使用合成版本的拷贝控制成员对对象进行拷贝、赋值或销毁。

练习 13.21:

由于合成版本满足这种情况下的所有需求,因此不需要定义自定义版本的拷贝控制成员。

练习 13.22:

#include<iostream>
using namespace std;

class HasPtr
{
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
	HasPtr& operator=(const HasPtr& hp)
	{
		auto newp = new string(*hp.ps);
		delete ps;
		ps = newp;
		i = hp.i;
		return *this;
	}
    //HasPtr& operator=(const HasPtr& hp)
	//{
	//	delete ps;
	//	// 如果hp和*this是同一个对象,我们就将从已释放的内存中拷贝数据!
	//	ps = new string(*hp.ps);
	//	i = hp.i;
	//	return *this;
	//}
	~HasPtr() { delete ps; }
private:
	string* ps;
	int i;
};

练习 13.23:

        对于一个赋值运算符来说,正确工作是非常重要的,即使是将一个对象赋予它自身,也要能正确工作。一个好的方法是在销毁左侧运算对象资源之前拷贝右侧运算对象。

练习 13.24:

        如果HasPtr没有定义析构函数,就会发生内存泄漏,因为成员变量ps是动态分配的对象,需要使用delete显式的去释放。如果HasPtr没有定义拷贝构造函数,可能会造成指针被delete两次,或使用悬空指针的情况。因为,使用合成的拷贝构造函数和拷贝复制运算符,只是简单拷贝指针成员,这意味着多个HasPtr对象可能指向相同的内存。

练习 13.25:

        拷贝构造函数和拷贝赋值运算符应该动态地为自己分配内存,而不是与右侧对象共享数据。
不需要析构函数是因为shared_ptr调用自己的析构函数来释放动态分配的内存。

练习 13.26:

#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<fstream>
using namespace std;

class ConstStrBlobPtr;
// StrBlob类
class StrBlob
{
public:
	typedef vector<string>::size_type size_type;
	friend class ConstStrBlobPtr;

	ConstStrBlobPtr begin()const;
	ConstStrBlobPtr end()const;

	StrBlob() : data(make_shared<vector<string>>()) { }
	StrBlob(initializer_list<string> il) :
		data(make_shared<vector<string>>(il)) { }
	// 拷贝构造函数
	StrBlob(const StrBlob& sb): data(make_shared<vector<string>>(*sb.data)){}
	// 拷贝赋值运算符
	StrBlob& operator=(const StrBlob& sb)
	{
		this->data = make_shared<vector<string>>(*sb.data);
		return *this;
	}
	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();
	}
	string& front()
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	const string& front()const
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	string& back()
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	const string& back()const
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}

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

class ConstStrBlobPtr
{
public:
	ConstStrBlobPtr() : curr(0) {}
	ConstStrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
	bool operator!=(const ConstStrBlobPtr& p) { return p.curr != curr; }
	const string& deref()const
	{
		auto p = check(curr, "dereference past end");
		return (*p)[curr];
	}
	ConstStrBlobPtr& incr()
	{
		check(curr, "increment past end of ConstStrBlobPtr");
		++curr;
		return *this;
	}
private:
	shared_ptr<vector<string>> check(size_t i, const string& msg)const
	{
		auto ret = wptr.lock();
		if (!ret)
			throw runtime_error("unbound ConstStrBlobPtr");
		if (i >= ret->size())
			throw out_of_range(msg);
		return ret;
	}
	weak_ptr<vector<string>> wptr;
	size_t curr;
};

ConstStrBlobPtr StrBlob::begin()const { return ConstStrBlobPtr(*this); }
ConstStrBlobPtr StrBlob::end()const { return ConstStrBlobPtr(*this, data->size()); }

练习 13.27:

#include<iostream>
#include<string>
using namespace std;

class HasPtr
{
public:
	// 构造函数分配新的string和新的计数器,将计数器置为1
	HasPtr(const string& s = string()) : 
		ps(new string(s)), i(0), use(new size_t(1)) {}
	HasPtr(const HasPtr& rhs) : 
		ps(rhs.ps), i(rhs.i), use(rhs.use) { ++*use; }
	HasPtr& operator=(const HasPtr& rhs)
	{
		// 递增右侧运算对象的引用计数
		++*use;
		// 递减本对象的引用计数,如果引用计数为0,释放释放ps和use的内存
		if (--*use == 0)
		{
			delete ps;
			delete use;
		}
		// 将数据从rhs拷贝到本对象
		this->ps = rhs.ps;
		this->i = rhs.i;
		this->use = rhs.use;
		return *this;
	}
	~HasPtr() 
	{ 
		// 如果引用计数变为0,释放ps和use的内存
		if (--*use == 0)
		{
			delete ps;
			delete use;
		}
	}
private:
	string* ps;
	int i;
	size_t* use;	// 用来记录有多少对象共享*ps的成员
};

练习 13.28:

#include<iostream>
#include<string>
using namespace std;

class TreeNode
{
public:
	TreeNode(const string& val = string(), int c = 0, TreeNode* l = nullptr, TreeNode* r = nullptr) : 
		value(val), count(c), left(new TreeNode(*l)), right(new TreeNode(*r)) {}
	TreeNode(const TreeNode& rhs) :
		value(rhs.value), count(rhs.count)
	{
		if (rhs.left != nullptr)
			left = new TreeNode(*rhs.left);
		else
			left = nullptr;
		if (rhs.right != nullptr)
			right = new TreeNode(*rhs.right);
		else
			right = nullptr;
	}
	TreeNode& operator=(const TreeNode& rhs)
	{
		
		value = rhs.value;
		count = rhs.count;

		if (rhs.left != nullptr)
		{
			auto newl = new TreeNode(*rhs.left);
			delete left;
			left = newl;
		}
		else
		{
			delete left;
			left = nullptr;
		}
		if (rhs.right != nullptr)
		{
			auto newr = new TreeNode(*rhs.right);
			delete right;
			right = newr;
		}
		else
		{
			delete right;
			right = nullptr;
		}
		return *this;
	}
	~TreeNode()
	{
			delete left;
			delete right;
	}
private:
	string value;
	int count;
	TreeNode* left;
	TreeNode* right;
};

class BinStrTree
{
public:
	BinStrTree(): root(new TreeNode()){}
	BinStrTree(const BinStrTree& rhs): root(new TreeNode(*rhs.root)){}
	BinStrTree& operator=(const BinStrTree& rhs)
	{
		auto new_root = new TreeNode(*rhs.root);
		delete root;
		root = new_root;
		return *this;
	}
	~BinStrTree() { delete root; }
private:
	TreeNode* root;
};

练习 13.29:

        因为该类中的数据成员是内置类型的,而内置类型是没有特定版本的swap的,所以对swap的调用会调用标准库的std::swap。

练习 13.30:

#include<iostream>
#include<string>
using namespace std;

class HasPtr
{
	friend ostream& print(ostream& os, HasPtr& hp);
	friend void swap(HasPtr& lhs, HasPtr& rhs);
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
	HasPtr& operator=(const HasPtr& hp)
	{
		auto newp = new string(*hp.ps);
		delete ps;
		ps = newp;
		i = hp.i;
		return *this;
	}
	~HasPtr() { delete ps; }
private:
	string* ps;
	int i;
};

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

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

int main()
{
	HasPtr hp1("Cpp");
	HasPtr hp2("Java");
	print(cout, hp1) << endl;
	print(cout, hp2) << endl;
	swap(hp1, hp2);
	print(cout, hp1) << endl;
	print(cout, hp2) << endl;
}

练习 13.31:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

class HasPtr
{
	friend ostream& print(ostream& os, HasPtr& hp);
	friend void swap(HasPtr& lhs, HasPtr& rhs);
	friend bool operator<(const HasPtr& lhs, const HasPtr& rhs);
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
	HasPtr& operator=(HasPtr hp)	// 值传递,不改变实参
	{
		swap(*this, hp);
		return *this;
	}
	~HasPtr() { delete ps; }
private:
	string* ps;
	int i;
};

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

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

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

int main()
{
	vector<HasPtr> hp_vec;
	string word;
	while (cin >> word)
		hp_vec.push_back(HasPtr(word));
	for (auto& elem : hp_vec)
		print(cout, elem) << endl;
	sort(hp_vec.begin(), hp_vec.end());
	for (auto& elem : hp_vec)
		print(cout, elem) << endl;

	return 0;
}

在赋值运算符中使用swap,调用sort的时候会执行swap。

练习 13.32:

        类值的HasPtr版本使用swap函数交换指针,而不是分配string的副本,从而避免内存分配是swap函数提高性能的原因。类指针的HasPtr版本交换的数据成员原本就是指针和整型,并没有优化的效果,所以没有得到益处。

练习 13.33:

        因为这些操作会改变给定的Folder。Folder类通过它的addMsg和remMsg成员分别添加或删除给定Message的指针。

练习 13.34 && 13.36 && 13.37:

#include<iostream>
#include<string>
#include<set>
#include<algorithm>
using namespace std;

class Folder;

class Message
{
	friend class Folder;
	friend void swap(Message& lhs, Message& rhs);
public:
	Message(const string& str = ""): contents(str){}
	Message(const Message&);
	Message& operator=(const Message&);
	~Message();
	// 从给定Folder集合中添加/删除本Message
	void save(Folder&);
	void remove(Folder&);
	void print() { cout << contents << endl; }

private:
	string contents;		// 实际消息文本
	set<Folder*> folders;	// 包含本message的folder
	// 将本Message添加到指向参数的Folder中
	void add_to_Folders(const Message&);
	// 从folders中的每个folder中删除本Message
	void remove_from_Folders();

	void addFldr(Folder* f) { this->folders.insert(f); }
	void remFldr(Folder* f) { this->folders.erase(f); }
};

class Folder
{
	friend class Message;
	friend void swap(Folder& lhs, Folder& rhs);
public:
	Folder() = default;
	Folder(const Folder&);
	Folder& operator=(const Folder&);
	~Folder();
	void print()
	{
		for (auto m : this->msgs)
			cout << m->contents << " ";
		cout << endl;
	}

private:
	set<Message*> msgs;
	void add_to_Message(const Folder&);
	void remove_from_Message();

	void addMsg(Message* m) { msgs.insert(m); }
	void remMsg(Message* m) { msgs.erase(m); }

};

// Message类的成员函数
// 将本Message添加到指向参数的Folder中
void Message::add_to_Folders(const Message& m)
{
	for (auto f : m.folders)	// 对每个包含m的folder
		f->addMsg(this);		// 向该folder添加一个指向本Message的指针
}
// 拷贝构造
Message::Message(const Message& rhs): contents(rhs.contents), folders(rhs.folders)
{
	this->add_to_Folders(rhs);
}
// 从对应的folders中删除本Message
void Message::remove_from_Folders()
{
	for (auto f : this->folders)// 对于folders中每个指针
		f->remMsg(this);		// 从该folder中删除本message
}
// 析构
Message::~Message()
{
	this->remove_from_Folders();
}
// 拷贝赋值
Message& Message::operator=(const Message& rhs)
{
	this->remove_from_Folders();
	this->contents = rhs.contents;
	this->folders = rhs.folders;
	this->add_to_Folders(rhs);	// 将本Message添加到那些Folder中
	return *this;
}
// 从给定Folder集合中添加本Message
void Message::save(Folder& f)
{
	this->folders.insert(&f);
	f.addMsg(this);
}
// 从给定Folder集合中删除本Message
void Message::remove(Folder& f)
{
	this->folders.erase(&f);
	f.remMsg(this);
}

void swap(Message& lhs, Message& rhs)
{
	// 将每个消息的指针从它(旧)所在Folder中删除
	lhs.remove_from_Folders();
	rhs.remove_from_Folders();
	// 交换contents和Folder指针
	swap(lhs.contents, rhs.contents);
	swap(lhs.folders, rhs.folders);
	// 将每个Message的指针添加到它的(新)Folder中
	lhs.add_to_Folders(lhs);
	rhs.add_to_Folders(rhs);
}

// Folder类的成员函数
void Folder::add_to_Message(const Folder& f)
{
	for (auto m : f.msgs)
		m->addFldr(this);
}
// 拷贝构造
Folder::Folder(const Folder& rhs): msgs(rhs.msgs)
{
	this->add_to_Message(rhs);
}

void Folder::remove_from_Message()
{
	for (auto m : this->msgs)
		m->remFldr(this);
}
// 析构
Folder::~Folder()
{
	this->remove_from_Message();
}
// 拷贝赋值
Folder& Folder::operator=(const Folder& rhs)
{
	this->remove_from_Message();
	this->msgs = rhs.msgs;
	this->add_to_Message(rhs);
	return *this;
}

void swap(Folder& lhs, Folder& rhs)
{
	lhs.remove_from_Message();
	rhs.remove_from_Message();
	swap(lhs.msgs, rhs.msgs);
	lhs.add_to_Message(lhs);
	rhs.add_to_Message(rhs);
}

int main()
{
	Message msg1("Cpp");
	Message msg2("Java");
	msg1.print();
	msg2.print();

	Folder fldr;
	msg1.save(fldr);
	msg2.save(fldr);
	fldr.print();

	msg1 = msg1;
	msg1.print();
	fldr.print();

	msg1 = msg2;
	msg1.print();
	msg2.print();
	fldr.print();

	return 0;
}

练习 13.35:

如果message的内容被改变,folders中的message内容不同步。

练习 13.38:

        在处理动态分配的内存时,拷贝并交换是一种很好的方式。在Message类中,没有动态分配任何内容。因此,使用这种习惯用法没有任何意义,而且由于返回的指针,实现起来会更加复杂。

练习 13.39 && 13.40:

#ifndef STRVEC_H_
#define STRVEC_H_

#include<string>
#include<memory>
#include<initializer_list>
using namespace std;

class StrVec
{
public:
	StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
	StrVec(const StrVec&);
	StrVec(initializer_list<string>);
	StrVec& operator=(const StrVec&);
	~StrVec();
	void push_back(const string&);
	size_t size()const { return first_free - elements; }
	size_t capacity()const { return cap - elements; }
	string* begin()const { return elements; }
	string* end()const { return first_free; }

	string& at(size_t pos) const { return *(elements + pos); }

	void reserve(size_t newcapacity);
	void resize(size_t count);
	void resize(size_t count, const string&);

private:
	allocator<string> alloc;	// 分配元素
	void chk_n_alloc()
	{
		if (size() == capacity())
			reallocate();
	}
	void reallocate();		// 分配内存并拷贝已有元素
	void alloc_n_move(size_t);	// 工具函数,分配内存并移动元素
    void range_initialize(const string*, const string*);

	// 工具函数,拷贝控制成员所使用
	pair<string*, string*> alloc_n_copy(const string* beg, const string* end);	// 开辟内存并拷贝元素
	void free();	// 销毁元素并释放内存

	string* elements;	// 指向首元素的指针
	string* first_free;	// 指向数组第一个空闲元素的指针
	string* cap;		// 指向数组尾后位置的指针
};

#endif
#include"StrVec.h"

pair<string*, string*> StrVec::alloc_n_copy(const string* beg, const string* end)
{
	// 分配空间保存给定范围中的元素
	auto pbeg = alloc.allocate(end - beg);
	auto pend = uninitialized_copy(beg, end, pbeg);
	// 返回一对pair,分别指向首元素和最后一个元素之后的位置
	return pair<string*, string*>(pbeg, pend);
}

void StrVec::free()
{
	if (elements)
	{
		for (auto p = first_free; p != elements;)
			alloc.destroy(--p);		// 逆序销毁元素
		alloc.deallocate(elements, cap - elements);	// 释放内存
	}
}

void StrVec::range_initialize(const string* beg, const string* end)
{
	auto newdata = alloc_n_copy(beg, end);
	elements = newdata.first;
	first_free = cap = newdata.second;
}

StrVec::StrVec(initializer_list<string> il)
{
	range_initialize(il.begin(), il.end());
}

StrVec::StrVec(const StrVec& rhs)
{
	range_initialize(rhs.begin(), rhs.end());
}

StrVec& StrVec::operator=(const StrVec& rhs)
{
	auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = newdata.first;
	first_free = cap = newdata.second;
	return *this;
}

StrVec::~StrVec()
{
	free();
}

void StrVec::push_back(const string& s)
{
	chk_n_alloc();	// 确保有空间容纳新元素
	// 在first_free指向的元素中构造s的副本
	alloc.construct(first_free++, s);
}

void StrVec::alloc_n_move(size_t newcapacity)
{
	// 分配新内存
	auto newdata = alloc.allocate(newcapacity);
	auto dest = newdata;	// 用来保存新数组下一个空闲位置
	for (auto beg = elements; beg != first_free; ++beg)
		alloc.construct(dest++, std::move(*beg)); // 调用move使用string的移动构造函数
	free();		// 释放旧内存空间
	// 更新指针
	elements = newdata;
	first_free = dest;
	cap = elements + newcapacity;
}

void StrVec::reallocate()	// 分配内存并拷贝已有元素
{
	// 分配当前大小2倍的内存空间
	auto newcapacity = size() ? 2 * size() : 1;
	alloc_n_move(newcapacity);
}

void StrVec::reserve(size_t newcapacity)
{
	if (newcapacity <= capacity())
		return;
	alloc_n_move(newcapacity);

}

void StrVec::resize(size_t count)
{
	resize(count, string());
}

void StrVec::resize(size_t count, const string& s)
{
	if (count > size())
	{
		if (count > capacity())
			reserve(2 * count);
		while (first_free != elements + count)
			alloc.construct(first_free++, s);
	}
	else if (count < size())
	{
		while (first_free != elements + count)
			alloc.destroy(--first_free);
	}
}

练习 13.41:

        因为first_free指向最后一个实际元素之后的位置,所以应该用后置递增运算依次添加元素,如果使用前置递增运算,中间会空出一个内存,这种情况是未定义的。

练习 13.42:

#ifndef TEXTQUERY_H_
#define TEXTQUERY_H_

#include<iostream>
#include<string>
#include<fstream>
#include<memory>
#include<set>
#include<map>
using namespace std;
#include"StrVec.h"

class QueryResult;

class TextQuery
{
public:
	TextQuery(ifstream&);
	QueryResult query(const string&)const;
private:
	shared_ptr<StrVec> file;
	map<string, shared_ptr<set<size_t>>> wm;
};

class QueryResult
{
	friend ostream& print(ostream&, const QueryResult&);
public:
	QueryResult(const string& s, shared_ptr<set<size_t>> plines, shared_ptr<StrVec> f) :
		sought(s), lines(plines), file(f) { }

private:
	string sought;
	shared_ptr<set<size_t>> lines;
	shared_ptr<StrVec> file;
};

ostream& print(ostream& os, const QueryResult& qr);

#endif
#include"TextQuery.h"
#include<sstream>
#include<algorithm>

TextQuery::TextQuery(ifstream& ifs) : file(new StrVec)
{
	size_t lineNo = 0;
	for (string line; getline(ifs, line); ++lineNo)
	{
		file->push_back(line);				// 保存此行文本
		istringstream line_iss(line);
		for (string word; line_iss >> word;)
		{
			// 避免读入标点符号
			auto punct_pos = find_if(word.begin(), word.end(), ispunct);
			if (punct_pos != word.end())
				word = string(word.begin(), punct_pos);
			// 如果单词不在wm中,以之为下标在wm中添加一项
			auto& lines = wm[word];
			if (!lines)	// 第一次遇到这个单词时,此指针为空
				lines.reset(new set<size_t>);
			lines->insert(lineNo);
		}
	}
}

QueryResult TextQuery::query(const string& sought)const
{
	// 如果未找到sought, 我们将返回指向此set的指针
	static shared_ptr<set<size_t>> nodata(new set<size_t>);
	auto loc = wm.find(sought);
	if (loc != wm.end())
		return QueryResult(sought, loc->second, file);
	else
		return QueryResult(sought, nodata, file);
}

// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
	os << qr.sought << " occurs " << qr.lines->size()
		<< (qr.lines->size() > 1 ? " times" : " time") << endl;
	// 打印单词出现的每一行
	for (auto line : *qr.lines)
		os << "\t(line " << line + 1 << ") " << qr.file->at(line) << endl;
	return os;
}
#include"TextQuery.h"
#include<iostream>

void runQueries(ifstream& infile)
{
	TextQuery tq(infile);	// 保存文件并建立查询map
	while (true)
	{
		cout << "enter word to look for, or \"q\" to quit: ";
		string s;
		if (cin >> s && s != "q")
			print(cout, tq.query(s)) << endl;
		else
			break;
	}
}

int main()
{
	ifstream file("storyDataFile.txt");
	runQueries(file);
}

练习 13.43:

for_each(elements, first_free, [this](string& s) {alloc.destroy(&s); });

练习 13.44:

#include<iostream>
#include<memory>
#include<algorithm>
using namespace std;

class String
{
	friend ostream& print(ostream& os, const String& s);
public:
	String(): elements(nullptr), end(nullptr){}
	String(const char*);
	String(const String&);
	String& operator=(const String&);
	~String();

	size_t size()const { return end - elements; }
	
private:
	pair<char*, char*> alloc_n_copy(const char*, const char*);
	void range_initializer(const char*, const char*);
	void free();

	allocator<char> alloc;
	char* elements;
	char* end;
};

pair<char*, char*> String::alloc_n_copy(const char* b, const char* e)
{
	auto str = alloc.allocate(e - b);
	return { str, std::uninitialized_copy(b, e, str) };
}

void String::free()
{
	if (elements)
	{
		for_each(elements, end, [this](char& c) { alloc.destroy(&c); });
		alloc.deallocate(elements, end - elements);
	}
}

void String::range_initializer(const char* b, const char* e)
{
	auto newStr = alloc_n_copy(b, e);
	elements = newStr.first;
	end = newStr.second;
}

String::String(const char* s)
{
	char* s1 = const_cast<char*>(s);
	while (*s1)
		++s1;
	range_initializer(s, s1);
}

String::String(const String& rhs)
{
	range_initializer(rhs.elements, rhs.end);
}

String& String::operator=(const String& rhs)
{
	auto newStr = alloc_n_copy(rhs.elements, rhs.end);
	free();
	elements = newStr.first;
	end = newStr.second;
	return *this;
}

String::~String() { free(); }

ostream& print(ostream& os, const String& s)
{
	for (auto beg = s.elements; beg != s.end; ++beg)
		os << *beg;
	return os;
}

练习 13.45:

  • 左值引用:可以绑定到左值的引用。还可以将const的左值引用绑定到一个右值上。
  • 右值引用: 必须绑定到右值的引用,指向将被销毁的对象。

        可以将右值引用绑定到要求转换的表达式、字面常量或是返回右值的表达式,但不能直接将一个右值引用绑定到一个左值。

练习 13.46:

int f();
vector<int> vi(100);
int&& r1 = f();
int& r2 = vi[0];
int& r3 = r1;
int&& r4 = vi[0] * f();

练习 13.47:

#ifndef STRING_HPP_
#define STRING_HPP_

#include<iostream>
#include<memory>
#include<algorithm>
using namespace std;

class String
{
	friend ostream& print(ostream& os, const String& s);
public:
	String() : elements(nullptr), end(nullptr) {}
	String(const char*);
	String(const String&);
	String& operator=(const String&);
	~String();

	size_t size()const { return end - elements; }

private:
	pair<char*, char*> alloc_n_copy(const char*, const char*);
	void range_initializer(const char*, const char*);
	void free();

	allocator<char> alloc;
	char* elements;
	char* end;
};

pair<char*, char*> String::alloc_n_copy(const char* b, const char* e)
{
	auto str = alloc.allocate(e - b);
	return { str, std::uninitialized_copy(b, e, str) };
}

void String::free()
{
	if (elements)
	{
		for_each(elements, end, [this](char& c) { alloc.destroy(&c); });
		alloc.deallocate(elements, end - elements);
	}
}

void String::range_initializer(const char* b, const char* e)
{
	auto newStr = alloc_n_copy(b, e);
	elements = newStr.first;
	end = newStr.second;
}

String::String(const char* s)
{
	char* s1 = const_cast<char*>(s);
	while (*s1)
		++s1;
	range_initializer(s, s1);
}

String::String(const String& rhs)
{
	range_initializer(rhs.elements, rhs.end);
	cout << "Copy construction" << endl;
}

String& String::operator=(const String& rhs)
{
	auto newStr = alloc_n_copy(rhs.elements, rhs.end);
	free();
	elements = newStr.first;
	end = newStr.second;
	cout << "Copy assignment" << endl;
	return *this;
}

String::~String() { free(); }

ostream& print(ostream& os, const String& s)
{
	for (auto beg = s.elements; beg != s.end; ++beg)
		os << *beg;
	return os;
}

#endif

练习 13.48:

#include"String.hpp"
#include<iostream>
#include<vector>

int main()
{
	const char* chs = "Hello Word";
	String s1("cpp");
	String s2(chs);
	String s3(s1);
	String s4 = s2;

	vector<String> StrVec;
	StrVec.push_back(s1);
	StrVec.push_back(s2);
	StrVec.push_back(s3);
	StrVec.push_back(s4);

	for (const auto& s : StrVec)
		print(cout, s) << endl;
}

练习 13.49:

#include<string>
#include<memory>
#include<initializer_list>
#include<algorithm>
using namespace std;

class StrVec
{
public:
	StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
	StrVec(const StrVec&);
	StrVec(initializer_list<string>);
	StrVec& operator=(const StrVec&);
	StrVec(StrVec&&)noexcept;
	StrVec& operator=(StrVec&&)noexcept;
	~StrVec();
	void push_back(const string&);
	size_t size()const { return first_free - elements; }
	size_t capacity()const { return cap - elements; }
	string* begin()const { return elements; }
	string* end()const { return first_free; }

	string& at(size_t pos) const { return *(elements + pos); }

	void reserve(size_t newcapacity);
	void resize(size_t count);
	void resize(size_t count, const string&);

private:
	allocator<string> alloc;	// 分配元素
	void chk_n_alloc()
	{
		if (size() == capacity())
			reallocate();
	}
	void reallocate();		// 分配内存并拷贝已有元素
	void alloc_n_move(size_t);	// 工具函数,分配内存并移动元素
	void range_initialize(const string*, const string*);

	// 工具函数,拷贝控制成员所使用
	pair<string*, string*> alloc_n_copy(const string* beg, const string* end);	// 开辟内存并拷贝元素
	void free();	// 销毁元素并释放内存

	string* elements;	// 指向首元素的指针
	string* first_free;	// 指向数组第一个空闲元素的指针
	string* cap;		// 指向数组尾后位置的指针
};


pair<string*, string*> StrVec::alloc_n_copy(const string* beg, const string* end)
{
	// 分配空间保存给定范围中的元素
	auto pbeg = alloc.allocate(end - beg);
	auto pend = uninitialized_copy(beg, end, pbeg);
	// 返回一对pair,分别指向首元素和最后一个元素之后的位置
	return pair<string*, string*>(pbeg, pend);
}

void StrVec::free()
{
	if (elements)
	{
		for_each(elements, first_free, [this](string& s) {alloc.destroy(&s); });
		alloc.deallocate(elements, cap - elements);	// 释放内存
	}
}

void StrVec::range_initialize(const string* beg, const string* end)
{
	auto newdata = alloc_n_copy(beg, end);
	elements = newdata.first;
	first_free = cap = newdata.second;
}

StrVec::StrVec(initializer_list<string> il)
{
	range_initialize(il.begin(), il.end());
}

StrVec::StrVec(const StrVec& rhs)
{
	range_initialize(rhs.begin(), rhs.end());
}

StrVec& StrVec::operator=(const StrVec& rhs)
{
	auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = newdata.first;
	first_free = cap = newdata.second;
	return *this;
}

StrVec::~StrVec()
{
	free();
}

void StrVec::push_back(const string& s)
{
	chk_n_alloc();	// 确保有空间容纳新元素
	// 在first_free指向的元素中构造s的副本
	alloc.construct(first_free++, s);
}

void StrVec::alloc_n_move(size_t newcapacity)
{
	// 分配新内存
	auto newdata = alloc.allocate(newcapacity);
	auto dest = newdata;	// 用来保存新数组下一个空闲位置
	for (auto beg = elements; beg != first_free; ++beg)
		alloc.construct(dest++, std::move(*beg)); // 调用move使用string的移动构造函数
	free();		// 释放旧内存空间
	// 更新指针
	elements = newdata;
	first_free = dest;
	cap = elements + newcapacity;
}

void StrVec::reallocate()	// 分配内存并拷贝已有元素
{
	// 分配当前大小2倍的内存空间
	auto newcapacity = size() ? 2 * size() : 1;
	alloc_n_move(newcapacity);
}

void StrVec::reserve(size_t newcapacity)
{
	if (newcapacity <= capacity())
		return;
	alloc_n_move(newcapacity);

}

void StrVec::resize(size_t count)
{
	resize(count, string());
}

void StrVec::resize(size_t count, const string& s)
{
	if (count > size())
	{
		if (count > capacity())
			reserve(2 * count);
		while (first_free != elements + count)
			alloc.construct(first_free++, s);
	}
	else if (count < size())
	{
		while (first_free != elements + count)
			alloc.destroy(--first_free);
	}
}

StrVec::StrVec(StrVec&& rhs)noexcept
	:elements(rhs.elements), first_free(rhs.first_free), cap(rhs.cap)
{
	// 令rhs进入这样的状态--对其运行析构函数是安全的
	rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
StrVec& StrVec::operator=(StrVec&& rhs)noexcept
{
	// 直接检查自赋值
	if (this != &rhs)
	{
		free();	// 释放已有元素
		elements = rhs.elements;	// 从rhs接管资源
		first_free = rhs.first_free;
		cap = rhs.cap;
		// 将rhs置于可析构状态
		rhs.elements = rhs.first_free = rhs.cap = nullptr;
	}
	return *this;
}
#ifndef STRING_H_
#define STRING_H_

#include<iostream>
#include<memory>
#include<algorithm>
using namespace std;

class String
{
	friend ostream& print(ostream& os, const String& s);
public:
	String() : elements(nullptr), end(nullptr) {}
	String(const char*);
	String(const String&);
	String& operator=(const String&);
	String(String&&)noexcept;
	String& operator=(String&&)noexcept;
	~String();

	size_t size()const { return end - elements; }

private:
	pair<char*, char*> alloc_n_copy(const char*, const char*);
	void range_initializer(const char*, const char*);
	void free();

	allocator<char> alloc;
	char* elements;
	char* end;
};

pair<char*, char*> String::alloc_n_copy(const char* b, const char* e)
{
	auto str = alloc.allocate(e - b);
	return { str, std::uninitialized_copy(b, e, str) };
}

void String::free()
{
	if (elements)
	{
		for_each(elements, end, [this](char& c) { alloc.destroy(&c); });
		alloc.deallocate(elements, end - elements);
	}
}

void String::range_initializer(const char* b, const char* e)
{
	auto newStr = alloc_n_copy(b, e);
	elements = newStr.first;
	end = newStr.second;
}

String::String(const char* s)
{
	char* s1 = const_cast<char*>(s);
	while (*s1)
		++s1;
	range_initializer(s, s1);
}

String::String(const String& rhs)
{
	range_initializer(rhs.elements, rhs.end);
	cout << "Copy construction" << endl;
}

String& String::operator=(const String& rhs)
{
	auto newStr = alloc_n_copy(rhs.elements, rhs.end);
	free();
	elements = newStr.first;
	end = newStr.second;
	cout << "Copy assignment" << endl;
	return *this;
}

String::String(String&& rhs)noexcept: elements(rhs.elements), end(rhs.end)
{
	rhs.elements = rhs.end = nullptr;
}

String& String::operator=(String&& rhs)noexcept
{
	if (this != &rhs)
	{
		free();
		elements = rhs.elements;
		end = rhs.end;
		rhs.elements = rhs.end = nullptr;
	}
	return *this;
}

String::~String() { free(); }

ostream& print(ostream& os, const String& s)
{
	for (auto beg = s.elements; beg != s.end; ++beg)
		os << *beg;
	return os;
}

#endif
#include<iostream>
#include<string>
#include<set>
#include<algorithm>
using namespace std;

class Folder;

class Message
{
	friend class Folder;
	friend void swap(Message& lhs, Message& rhs);
public:
	Message(const string& str = "") : contents(str) {}
	Message(const Message&);
	Message& operator=(const Message&);
	Message(Message&&);
	Message& operator=(Message&&);
	~Message();
	// 从给定Folder集合中添加/删除本Message
	void save(Folder&);
	void remove(Folder&);
	void print() { cout << contents << endl; }

private:
	string contents;		// 实际消息文本
	set<Folder*> folders;	// 包含本message的folder
	// 将本Message添加到指向参数的Folder中
	void add_to_Folders(const Message&);
	// 从folders中的每个folder中删除本Message
	void remove_from_Folders();
	void move_Folders(Message*);

	void addFldr(Folder* f) { this->folders.insert(f); }
	void remFldr(Folder* f) { this->folders.erase(f); }
};

class Folder
{
	friend class Message;
	friend void swap(Folder& lhs, Folder& rhs);
public:
	Folder() = default;
	Folder(const Folder&);
	Folder& operator=(const Folder&);
	~Folder();
	void print()
	{
		for (auto m : this->msgs)
			cout << m->contents << " ";
		cout << endl;
	}

private:
	set<Message*> msgs;
	void add_to_Message(const Folder&);
	void remove_from_Message();

	void addMsg(Message* m) { msgs.insert(m); }
	void remMsg(Message* m) { msgs.erase(m); }

};

// Message类的成员函数
// 将本Message添加到指向参数的Folder中
void Message::add_to_Folders(const Message& m)
{
	for (auto f : m.folders)	// 对每个包含m的folder
		f->addMsg(this);		// 向该folder添加一个指向本Message的指针
}
// 拷贝构造
Message::Message(const Message& rhs) : contents(rhs.contents), folders(rhs.folders)
{
	this->add_to_Folders(rhs);
}
// 从对应的folders中删除本Message
void Message::remove_from_Folders()
{
	for (auto f : this->folders)// 对于folders中每个指针
		f->remMsg(this);		// 从该folder中删除本message
}
// 析构
Message::~Message()
{
	this->remove_from_Folders();
}
// 拷贝赋值
Message& Message::operator=(const Message& rhs)
{
	this->remove_from_Folders();
	this->contents = rhs.contents;
	this->folders = rhs.folders;
	this->add_to_Folders(rhs);	// 将本Message添加到那些Folder中
	return *this;
}

void Message::move_Folders(Message* m)
{
	folders = std::move(m->folders);	// 接管m的folders
	for (auto f : folders)
	{
		f->remMsg(m);		// 从Folder中删除旧Message
		f->addMsg(this);	// 将本Message添加到Folder中
	}
	m->folders.clear();		// 确保销毁m是无害的
}

Message::Message(Message&& rhs): contents(std::move(rhs.contents))
{
	move_Folders(&rhs);
}

Message& Message::operator=(Message&& rhs)
{
	if (this != &rhs)
	{
		remove_from_Folders();
		contents = std::move(rhs.contents);
		move_Folders(&rhs);
	}
	return *this;
}

// 从给定Folder集合中添加本Message
void Message::save(Folder& f)
{
	this->folders.insert(&f);
	f.addMsg(this);
}
// 从给定Folder集合中删除本Message
void Message::remove(Folder& f)
{
	this->folders.erase(&f);
	f.remMsg(this);
}

void swap(Message& lhs, Message& rhs)
{
	// 将每个消息的指针从它(旧)所在Folder中删除
	lhs.remove_from_Folders();
	rhs.remove_from_Folders();
	// 交换contents和Folder指针
	swap(lhs.contents, rhs.contents);
	swap(lhs.folders, rhs.folders);
	// 将每个Message的指针添加到它的(新)Folder中
	lhs.add_to_Folders(lhs);
	rhs.add_to_Folders(rhs);
}

// Folder类的成员函数
void Folder::add_to_Message(const Folder& f)
{
	for (auto m : f.msgs)
		m->addFldr(this);
}
// 拷贝构造
Folder::Folder(const Folder& rhs) : msgs(rhs.msgs)
{
	this->add_to_Message(rhs);
}

void Folder::remove_from_Message()
{
	for (auto m : this->msgs)
		m->remFldr(this);
}
// 析构
Folder::~Folder()
{
	this->remove_from_Message();
}
// 拷贝赋值
Folder& Folder::operator=(const Folder& rhs)
{
	this->remove_from_Message();
	this->msgs = rhs.msgs;
	this->add_to_Message(rhs);
	return *this;
}

void swap(Folder& lhs, Folder& rhs)
{
	lhs.remove_from_Message();
	rhs.remove_from_Message();
	swap(lhs.msgs, rhs.msgs);
	lhs.add_to_Message(lhs);
	rhs.add_to_Message(rhs);
}

练习 13.50:

#include"String.h"
#include<iostream>
#include<vector>

int main()
{
	const char* chs = "Hello Word";
	String s1("cpp");
	String s2(chs);
	String s3(s1);			// Copy construction
	String s4;
	s4 = s2;				// Copy assignment

	vector<String> StrVec;
	StrVec.push_back(s1);	// Copy construction
	StrVec.push_back(s2);	// Copy construction + Move construction
	StrVec.push_back(s3);	// Copy construction + 2 * Move construction
	StrVec.push_back(s4);	// Copy construction + 3 * Move construction

	for (const auto& s : StrVec)
		print(cout, s) << endl;
}

练习 13.51:

unique_ptr<int> clone(int p) {
    return unique_ptr<int>(new int(p));
}

        调用clone的结果是右值,因此它使用移动赋值运算符而不是拷贝赋值运算符。因此,它是合法的,可以正确工作。

练习 13.52:

HasPtr& operator=(HasPtr rhs)
{
    swap(*this, rhs);
    return *this;
}

        rhs是一个非引用参数,这意味着此参数是拷贝初始化的。依赖于实参的类型,拷贝初始化要么使用拷贝构造函数要么使用移动构造函数——左值被拷贝,右值被移动。

hp = hp2;    // hp2是一个左值; hp2通过拷贝构造函数来拷贝
hp = std::move(hp2)    // 移动构造函数移动hp2

练习 13.53:

        HasPtr的赋值运算符调用拷贝构造(移动构造)和swap,而它的拷贝赋值运算符调用拷贝构造,移动赋值运算符调用移动构造,所以HasPtr的赋值运算符底层效率并不理想。

#include<iostream>
#include<string>
using namespace std;

class HasPtr
{
	friend ostream& print(ostream& os, HasPtr& hp);
	friend void swap(HasPtr& lhs, HasPtr& rhs);
public:
	HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
	HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) 
	{
		cout << "copy constructor" << endl;
	}
	HasPtr(HasPtr&& hp)noexcept : ps(hp.ps), i(hp.i) 
	{ 
		hp.ps = nullptr; 
		cout << "move constructor" << endl;
	}
	// 赋值运算符既是移动赋值运算符,也是拷贝赋值运算符
	HasPtr& operator=(HasPtr rhs)
	{
		swap(*this, rhs);
		return *this;
	}
	// 拷贝赋值运算符
	//HasPtr& operator=(const HasPtr& rhs)
	//{
	//	auto newp = new string(*rhs.ps);
	//	delete ps;
	//	ps = newp;
	//	i = rhs.i;
	//	cout << "copy assignment" << endl;
	//	return *this;
	//}
	// 移动赋值运算符
	//HasPtr& operator=(HasPtr&& rhs)noexcept
	//{
	//	if (this != &rhs)
	//	{
	//		delete ps;
	//		ps = rhs.ps;
	//		i = rhs.i;
	//		rhs.ps = nullptr;
	//		cout << "move assignment" << endl;
	//	}
	//	return *this;
	//}
	~HasPtr() { delete ps; }
private:
	string* ps;
	int i;
};

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

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

int main()
{
	HasPtr hp1("Cpp");
	HasPtr hp2("Java");
	print(cout, hp1) << endl;
	print(cout, hp2) << endl;
	hp1 = hp2;				// 调用copy assignment + swap
	print(cout, hp1) << endl;
	hp1 = std::move(hp2);	// 调用move assignment + swap
	print(cout, hp1) << endl;

	return 0;
}

练习 13.54:

// 有多个"="运算符与这些操作数匹配:
// HasPtr& operator=(HasPtr rhs)    HasPtr& operator=(HasPtr&& rhs)
hp1 = std::move(hp2);

练习 13.55:

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

练习 13.56:

ret是一个左值,返回他的sorted函数,代码将陷入递归,并导致致命的堆栈溢出,程序异常终止。

练习 13.57:

调用右值版本的sorted函数,程序正常运行。

练习 13.58:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class Foo
{
public:
	Foo sorted()&&;
	Foo sorted()const&;
private:
	vector<int> data;
};

// 本对象为右值,因此可以原址排序
Foo Foo::sorted()&&
{
	sort(data.begin(), data.end());
	cout << "sorted()&&" << endl;
	return *this;
}

// 本对象是const或是一个左值,那种情况我们都不能对其进行原址排序
Foo Foo::sorted()const&
{
	/*Foo ret(*this);
	sort(ret.data.begin(), ret.data.end());
	return ret;*/

	cout << "sorted()const&" << endl;

	/*Foo ret(*this);
	return ret.sorted();*/

	return Foo(*this).sorted();
}

int main()
{
	Foo().sorted();

	Foo f;
	f.sorted();

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值