C++Primer 第十三章 拷贝控制 习题答案

13.1

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
1.使用=号
2.将一个对象作为实参传递给一个非引用类型的形参
3.从一个返回类型为非引用类型的函数返回一个对象
4.从一个花括号初始化一个数组中的元素或一个聚合类中的元素

13.2

拷贝构造函数被用来初始化非引用类型参数,自己的参数即形参必须是引用类型。否则为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。

12.3

当我们拷贝StrBlob时, shared_ptr 的引用计数加1。
当我们拷贝 StrBlobPtr 时,引用计数不会变化。

12.4

#include<iostream>
static int cnt = 0;
class Point
{
public:
	Point(){}
	Point(const Point&p)
	{
		++cnt;
		std::cout << "拷贝构造函数已经调用:" << cnt << "次" << std::endl;
	}
};
Point Global;
Point foo_bar(Point arg)// 1
{
	Point local = arg, *heap = new Point(Global);//2,3
	*heap = local;
	Point pa[4] = { local,*heap };//4,5
	return *heap;//6
}int main()
{
	Point arg;
	foo_bar(arg);
	return 0;
}

13.5

#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
	HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
	HasPtr(const HasPtr&);
private:
	std::string *ps;
	int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
	ps = new std::string(*has.ps);
	i = has.i;
}

13.6

拷贝复制运算符接受一个与其所在类相同类型的参数,同时为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。
对类对象进行赋值操作时,会使用拷贝赋值运算符。
如果一个类未定义自己的拷贝复制运算符,编译器会为他生成一个合成拷贝赋值运算符。

13.7

前者赋值计数会加一,后者赋值计数不会增加

13.8

#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
	HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
	HasPtr(const HasPtr&);
	//重载赋值运算符
	HasPtr& operator=(const HasPtr&);
private:
	std::string *ps;
	int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
	ps = new std::string(*has.ps);
	i = has.i;
}
HasPtr& HasPtr::operator=(const HasPtr&has) 
{
	std::string* temp = new std::string(*has.ps);
	delete ps;
	ps = has.ps;
	i = has.i;
	return *this;
}

13.9

析构函数释放对象使用的资源,并销毁对象的非static数据成员。
在对象的最后一次使用之后,进行收尾工作。
当一个类未定义自己的析构函数时。

13.10

前者会递减其引用计数。
后者被销毁时引用计数没有影响。

13.11

	~HasPtr() { 
		if(!ps){
		delete ps;
		ps = nullptr;}
	}

13.12

3次。
accum,iem1,item2.
函数的参数 accum 是按值传递的,如果是值传递,参数在函数调用时会被复制一份,函数结束时,这个副本也会被销毁。因此,即使accum在外部定义,在函数调用时作为参数传递给了fcn函数,也会在函数结束时被销毁。

13.13

X.h

#pragma once
#include<iostream>
class X
{
public:
	X() { std::cout << "构造函数X()" << std::endl; }
	X(const X&) { std::cout << "拷贝构造函数X(const X&)" << std::endl; }
	X& operator=(const X &rhs)
	{
		std::cout << "拷贝赋值运算符:" << std::endl;
		return *this;
	}
	~X() { std::cout << "析构函数:" << std::endl; }
};

main.cpp

#include"X.h"
int main()
{
	X x;
	X x2(x);
	X x3;
	x3 = x;
	return 0;
}

13.14

三个相同的数字。

13.15

会改变,在调用f函数时拷贝构造函数会赋予一个新的序号。输出结果时三个不一样的数组。

13.16

会改变,会输出a,b,c原来的值.

13.17

#include<iostream>
class numbered
{
public:
	numbered() { mysn = unique++; }
	numbered(numbered&) { mysn = ++unique; }
	int mysn;
	static int unique;
};
int numbered::unique = 0;
void f(numbered s)
{
	std::cout << s.mysn << std::endl;
}
int main()
{
	numbered a, b = a, c = b;
	f(a), f(b), f(c);
	return 0;
}

13.18

#pragma once
#include<string>
class Employee
{
public:
	Employee(){}
	Employee(std::string name_) :name(name_) { id = count++; }
	Employee(const Employee& n) 
{ 
	this->name = n.name; 
	id = ++count;
}
private:
	std::string name;
	int id;
	int static count;
};
int Employee::count = 10000;

13.19

需要,否则会造成浅拷贝的问题,即多个对象指向同一片内存空间,在对象销毁时导致多次山粗

13.20

#pragma once
#include<string>
class Employee
{
public:
	Employee(){}
	Employee(std::string name_) :name(name_) { id = count++; }
	Employee(const Employee& n) 
    { 
	this->name = n.name; 
	id = ++count;
     }
	Employee& operator=(const Employee& n)
	{
		this->name = n.name;
		this->id = ++count;
		return *this;
	}
private:
	std::string name;
	int id;
	int static count;
};
int Employee::count = 10000;

13.20

编译器会给他们定义合成版本,拷贝会使计数器加一,销毁会减一。

13.21

不需要,因为只能指针会控制他们的销毁。

13.22

#pragma once
#include<string>
#include<memory>
class HasPtr
{
public:
	HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0){}
	HasPtr(const HasPtr&);
	~HasPtr() { 
		if (!ps) {
			delete ps;
			ps = nullptr;
		}
	}
	//重载赋值运算符
	HasPtr& operator=(const HasPtr&);



private:
	std::string *ps;
	int i;
};
HasPtr::HasPtr(const HasPtr& has )
{
	ps = new std::string(*has.ps);
	i = has.i;
}
HasPtr& HasPtr::operator=(const HasPtr&has) 
{
	std::string* temp = new std::string(*has.ps);
	delete ps;
	ps = has.ps;
	i = has.i;
	return *this;
}

13.24

未定义析构函数,会发生内存泄漏的问题,编译器的合成析构函数无法删除智能指针。
未定义拷贝构造函数,会发生浅拷贝的问题,合成拷贝构造函数会使两个指针指向同一片内存空间,删除的时候会删除两次。

13.25

需要跟13.22一样的操作,先拷贝,再删除,在赋值,不需要析构是因为只能指针有自己的计数功能。

13.26

#pragma once
#include <string>
#include <initializer_list>
#include <memory>
#include <vector>
#include <stdexcept>

class StrBlobPtr;

class StrBlob
{
public:
	friend class StrBlobPtr;
	typedef std::vector<std::string>::size_type size_type;
	StrBlob();
	StrBlob(std::initializer_list<std::string> il);
	StrBlob(const StrBlob&);
	StrBlob& operator=(const StrBlob&);
	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();
	std::string& back();
	const std::string& front() const;
	const std::string& back() const;
	StrBlobPtr begin();
	StrBlobPtr end();
private:
	std::shared_ptr<std::vector<std::string>> data;
	void check(size_type i, const std::string &msg) const;
};

StrBlob::StrBlob(const StrBlob&rhs)
{
	data = std::make_shared<std::vector<std::string>>(*rhs.data);
}
StrBlob& StrBlob::operator=(const StrBlob&rhs)
{
	data = std::make_shared<std::vector<std::string>>(*rhs.data);
	return *this;
}

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

std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::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::string& StrBlobPtr::deref() const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];
}

StrBlobPtr& StrBlobPtr::incr()
{
	check(curr, "increment past end of StrBlobPtr");
	++curr;
	return *this;
}

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

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

std::string & StrBlob::front()
{
	check(0, "front on empty StrBlob");
	return data->front();
}

std::string & StrBlob::back()
{
	check(0, "back on empty StrBlob");
	return data->back();
}

const std::string& StrBlob::front() const
{
	check(0, "front on empty StrBlob");
	return data->front();
}

const std::string& StrBlob::back() const
{
	check(0, "back on empty StrBlob");
	return data->back();
}

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

StrBlobPtr StrBlob::begin()const { return StrBlobPtr(*this); }

StrBlobPtr StrBlob::end()const
{
	auto ret = StrBlobPtr(*this, data->size());
	return ret;
}

13.27

#pragma once
#include<string>
#include<memory>
#include<Cstddef>
class HasPtr
{
public:
	// 构造函数分配新的string和新的计数器,将计数器置为1
	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&);
	//析构函数,计数器为0,清空
	~HasPtr()
	{
		if (--*use == 0)
		{
			delete ps;// 释放string内存
			delete use; // 释放计数器内存
		}
	}
private:
	std::string *ps;
	int i;
	size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
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;
}

13.28

tree.h

#pragma once
#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=(TreeNode&);
	~TreeNode()
	{
		if (*--count == 0)
		{
			delete left;
			delete right;
			delete count;
		}
	}
private:
	string value;
	int *count;
	TreeNode *left;
	TreeNode *right;
};
class BinStrTree
{
public:
	BinStrTree():root(new TreeNode()){}
	BinStrTree(const BinStrTree&bst) :root(new TreeNode(*bst.root)){}
	BinStrTree& operator=(const BinStrTree&);
	~BinStrTree()
	{
		delete root;
	}
private:
	TreeNode *root;
};

main.cpp

#include"Tree.h"
TreeNode& TreeNode::operator=(TreeNode&rhs)
{
	++*rhs.count;
	if (*--count == 0)
	{
		delete left;
		delete right;
		delete count;
	}
	value = rhs.value;
	left = rhs.left;
	right = rhs.right;
	count = rhs.count;
	return *this;
}
BinStrTree& BinStrTree::operator=(const BinStrTree&bst)
{
	TreeNode *new_root = new TreeNode(*bst.root);
	delete root;
	root = new_root;
	return *this;
}
int main()
{
	return 0;
}

13.29

有三个版本的swap
swap(HasPtr&lhs, HasPtr&rhs)是我们定义的;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);是std自带的。

13.30

#pragma once
#include<string>
#include<memory>
#include<iostream>
#include<Cstddef>
class HasPtr
{
	friend void swap(HasPtr&, HasPtr&);
public:
	// 构造函数分配新的string和新的计数器,将计数器置为1
	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&);
	//析构函数,计数器为0,清空
	~HasPtr()
	{
		if (--*use == 0)
		{
			delete ps;// 释放string内存
			delete use; // 释放计数器内存
		}
	}
private:
	std::string *ps;
	int i;
	size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
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);
	swap(lhs.i, rhs.i);
	std::cout << "swap函数调用" << std::endl;
}

13.31

hasptr.h

#pragma once
#include<string>
#include<memory>
#include<iostream>
#include<Cstddef>
#include<vector>
class HasPtr
{
	friend void swap(HasPtr&, HasPtr&);
	friend bool operator<(const HasPtr&lhs, const HasPtr&rhs);
public:
	// 构造函数分配新的string和新的计数器,将计数器置为1
	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&);
	//析构函数,计数器为0,清空
	~HasPtr()
	{
		if (--*use == 0)
		{
			delete ps;// 释放string内存
			delete use; // 释放计数器内存
		}
	}
	void show()const
	{
		std::cout << *ps << std::endl;
	}
private:
	std::string *ps;
	int i;
	size_t *use; // 用来记录有多少个对象共享*ps的成员
};
// 拷贝赋值运算符必须递增右侧运算符的计数,递减左侧运算符的计数
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;
}
bool operator<(const HasPtr&lhs, const HasPtr&rhs)
{
	return *lhs.ps < *rhs.ps;
}
inline void swap(HasPtr&lhs, HasPtr&rhs)
{
	using std::swap;
	swap(lhs.ps, rhs.ps);
	swap(lhs.i, rhs.i);
	std::cout << "swap函数调用" << std::endl;
}

main.cpp

#include"HasPtr.h"
#include<vector>
#include<algorithm>
int main()
{
	HasPtr h1("aaa");
	HasPtr h2("bbb");
	HasPtr h3("ccc");
	std::vector<HasPtr> v1{ h1,h3,h2 };
	std::sort(v1.begin(), v1.end());
	for (auto &i : v1)
		i.show();
	return 0;
}

13.32

类值版本的swap通过指针交换内存地址,不用开辟新的内存空间从而受益,
类指针版本可以直接交换地址,所以不会收益。

13.33

因为save和Move需要向folder中插入或删除某个元素。

13.34 &

36

#pragma once
#include<string>
#include<set>
class Folder;
class Message
{
	friend class Folder;
public:
	explicit Message(const std::string &str = " "):
		contents(str){}
	//考本控制成员,用来管理本message的指指针
	Message(const Message&);      //拷贝构造函数
	Message& operator=(Message&);//拷贝赋值运算符
	~Message();//析构函数
	//从给定folder中添加、删除本message
	void save(Folder&);
	void remove(Folder&);
	// 通过定义swap,避免对contents和folders成员进行不必要的拷贝
	void swap(Message &lhs, Message &rhs);

private:
	std::string contents; //包含实际文本
	std::set<Folder*> folders; //包含本message的folder
	//构造函数使用的工具函数
	//将本message添加到对应参数的folder中
	void add_to_Folder(const Message&);
	//从folder中的每个folder中删除本message
	void remove_form_Folders();
};
class Folder
{
public:
	void addMsg(Message*); //将本message添加到message的集合中
	void remMsg(Message*); //将本message从message的集合中取出
private:
	std::set<Message*>mgs;
};

void Message::save(Folder &f)
{
	folders.insert(&f);  //将给定Folder添加到我们的F列表中
	f.addMsg(this);//将本message添加到message的集合中
}
void Message::remove(Folder &f)
{
	folders.erase(&f);  //将给定Folder添加到我们的F列表中
	f.remMsg(this);  //将本message从message的集合中取出
}

//将本Message添加到指向M的Folder中
void Message::add_to_Folder(const Message&m)
{
	for (auto f : m.folders) //对每个包含m的Folder
		f->addMsg(this);   //向该Folder添加一个指向本Message的指针
}
//拷贝构造函数 拷贝给定对象的数据成员
Message::Message(const Message& m):contents(m.contents),folders(m.folders)
{
	add_to_Folder(m);  //将本消息添加到指向m的Folder中
}
//从对应的Folder中删除本message
void Message::remove_form_Folders() 
{
	for (auto f : folders)
		f->remMsg(this);
}
//析构函数
Message::~Message()
{
	remove_form_Folders();
}
//拷贝赋值运算符
Message& Message::operator=(Message&rhs)
{
	//先删除指针
	remove_form_Folders();//更新已有Folder
	contents = rhs.contents; //拷贝实际内容
	folders = rhs.folders; //从rhs拷贝Folder指针
	add_to_Folder(rhs); //将本message添加到f中
	return *this;
}

void Message::swap(Message &lhs, Message &rhs)
{
	using std::swap;
	//将每个消息的指针从它原来所在的Floders中删除
	for (auto f : lhs.folders)
		f->remMsg(&lhs);
	for (auto f : rhs.folders)
		f->remMsg(&rhs);
	swap(lhs.folders, rhs.folders);
	swap(lhs.contents, rhs.contents);
	// 将每个消息的指针添加到新的Floders中
	for (auto f : lhs.folders)
		f->addMsg(&lhs);
	for (auto f : rhs.folders)
		f->addMsg(&rhs);
}

void Folder::addMsg(Message* m) //将本message添加到message的集合中
{
	mgs.insert(m);
}

void Folder::remMsg(Message* m) //将本message从message的集合中取出
{
	mgs.erase(m);
}

13.35

在赋值以后Message和Folder将会不同步

13.37

#pragma once
#include<string>
#include<set>
class Folder;
class Message
{
	friend class Folder;
public:
	explicit Message(const std::string &str = " "):
		contents(str){}
	//考本控制成员,用来管理本message的指指针
	Message(const Message&);      //拷贝构造函数
	Message& operator=(Message&);//拷贝赋值运算符
	~Message();//析构函数
	//从给定folder中添加、删除本message
	void save(Folder&);
	void remove(Folder&);
	// 通过定义swap,避免对contents和folders成员进行不必要的拷贝
	void swap(Message &lhs, Message &rhs);

private:
	std::string contents; //包含实际文本
	std::set<Folder*> folders; //包含本message的folder
	//构造函数使用的工具函数
	//将本message添加到对应参数的folder中
	void add_to_Folder(const Message&);
	//从folder中的每个folder中删除本message
	void remove_form_Folders();
	//向folder中添加或删除一个给定的Folder*
	void addFldr(Folder *f) { folders.insert(f); }
	void remFldr(Folder *f) { folders.erase(f); }
};
class Folder
{
	friend class Message;
public:
	Folder() = default;
	Folder(const Folder&);
	Folder& operator=(const Folder&);
	~Folder();
	void addMsg(Message*); //将本message添加到message的集合中
	void remMsg(Message*); //将本message从message的集合中取出
private:
	std::set<Message*>mgs;
	//向添加的Folder*里面加入message
	void add_to_message(const Folder&);
	void remove_from_message();
};

void Message::save(Folder &f)
{
	folders.insert(&f);  //将给定Folder添加到我们的F列表中
	f.addMsg(this);//将本message添加到message的集合中
}
void Message::remove(Folder &f)
{
	folders.erase(&f);  //将给定Folder添加到我们的F列表中
	f.remMsg(this);  //将本message从message的集合中取出
}

//将本Message添加到指向M的Folder中
void Message::add_to_Folder(const Message&m)
{
	for (auto f : m.folders) //对每个包含m的Folder
		f->addMsg(this);   //向该Folder添加一个指向本Message的指针
}
//拷贝构造函数 拷贝给定对象的数据成员
Message::Message(const Message& m):contents(m.contents),folders(m.folders)
{
	add_to_Folder(m);  //将本消息添加到指向m的Folder中
}
//从对应的Folder中删除本message
void Message::remove_form_Folders() 
{
	for (auto f : folders)
		f->remMsg(this);
}
//析构函数
Message::~Message()
{
	remove_form_Folders();
}
//拷贝赋值运算符
Message& Message::operator=(Message&rhs)
{
	//先删除指针
	remove_form_Folders();//更新已有Folder
	contents = rhs.contents; //拷贝实际内容
	folders = rhs.folders; //从rhs拷贝Folder指针
	add_to_Folder(rhs); //将本message添加到f中
	return *this;
}

void Message::swap(Message &lhs, Message &rhs)
{
	using std::swap;
	//将每个消息的指针从它原来所在的Floders中删除
	for (auto f : lhs.folders)
		f->remMsg(&lhs);
	for (auto f : rhs.folders)
		f->remMsg(&rhs);
	swap(lhs.folders, rhs.folders);
	swap(lhs.contents, rhs.contents);
	// 将每个消息的指针添加到新的Floders中
	for (auto f : lhs.folders)
		f->addMsg(&lhs);
	for (auto f : rhs.folders)
		f->addMsg(&rhs);
}

void Folder::addMsg(Message* m) //将本message添加到message的集合中
{
	mgs.insert(m);
}

void Folder::remMsg(Message* m) //将本message从message的集合中取出
{
	mgs.erase(m);
}

void Folder::add_to_message(const Folder& f)
{
	for (auto m : f.mgs)
		m->addFldr(this);
}
void Folder::remove_from_message()
{
	for (auto m : mgs)
		m->remFldr(this);
}
Folder::~Folder()
{
	remove_from_message();
}

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

13.38

swap主要是用于动态内存时,这里并没有使用动态内存。

13.39

头文件

#pragma once
#include<string>
#include<memory>
/*实现vector的简化版本
用于分配动态内存*/
class StrVec
{
public:
	StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
	//老三样
	StrVec(const StrVec&);
	StrVec& operator=(const StrVec&);
	~StrVec();

	void push_back(const std::string&);//拷贝元素
	size_t size()const { return first_free - elements; } //实际大小
	size_t capacity() const { return cap - first_free; }//容量
	std::string *begin() const { return elements; }
	std::string *end() const { return first_free; }//相当于get函数

	void reserve(size_t  new_cap);
	void resize(size_t  count);
	void resize(size_t count, const std::string&);
private:
	std::allocator<std::string> alloc;// 分配元素
	// 被添加元素的函数所使用
	void chk_n_alloc() {	if (size() == capacity()) reallocate();}

	// 工具函数,被三拷贝函数使用
	std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);
	void free(); //销毁元素并释放内存
	void reallocate(); //获得更多内存并拷贝已有元素
	void alloc_n_move(size_t new_cap);
	//三个模仿vector迭代器的指针
	std::string *elements; //指向数组首元素的指针
	std::string *first_free; //指向实际元素之后的第一个位置
	std::string *cap;// 指向数组尾后的指针
};

源文件

#include"StrVec.h"
void StrVec::push_back(const std::string &s)
{
	chk_n_alloc(); //check一下是否内存已满
	alloc.construct(first_free++, s); //使用后置++
}
std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string*b, const std::string*e)
{
	//分配空间保存给定范围中的元素
	auto data = alloc.allocate(e - b);
	// 初始化返回一个pair,该pair由data和copy的返回值构成
	// 返回的pair的first成员指向首地址,second成员返回的是尾后的位置
	return { data,std::uninitialized_copy(b,e,data) };
}
void StrVec::free()
{
	// 不能传递给deallocata一个空指针
	if (elements)
	{
		//逆序销毁旧元素
		for (auto p = first_free; p != elements;)
			alloc.destroy(--p);
		alloc.deallocate(elements, cap - elements);
	}
}
StrVec::StrVec(const StrVec&s)
{
	//调用alloc_n_copy
	auto newdata = alloc_n_copy(s.begin(), s.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}
StrVec::~StrVec()
{
	free();
}
StrVec& StrVec::operator=(const StrVec&rhs)
{
	/*newdata 本身不是左侧运算符自身内存的一部分。
	newdata.first 和 newdata.second 存储的是动态内存的指针,
	这些指针指向的内存块是通过 allocator 对象的 allocate 成员函数从堆中动态分配的,不是位于 StrVec 对象内部的内存块。
	因此,在 free 函数中销毁元素和释放内存时,newdata 不会受到影响,也不会被释放掉。*/
	auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = newdata.first;
	first_free = cap = newdata.second;
	return *this;
}
void StrVec::reallocate()
{
	//分配当前大小两倍的内存空间
	auto newcapacity = size() ? 2 * size() : 1;
	//分配新内存
	auto newdata = alloc.allocate(newcapacity);
	// 将数据从就内存转移到新内存
	auto dest = newdata; //指向新数组中下一个空闲位置
	auto elem = elements;//就数组中下一个元素
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));
	free();
	elements = newdata;
	first_free = dest;
	cap = elements + newcapacity;
}
void StrVec::alloc_n_move(size_t new_cap)
{
	auto newdata = alloc.allocate(new_cap);
	auto dest = newdata;
	auto elem = elements;
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));
	free();
	elements = newdata;
	first_free = dest;
	cap = elements + new_cap;
}
void StrVec::reserve(size_t newcap)
{
	if (newcap <= capacity()) return;
	alloc_n_move(newcap);
}
void StrVec::resize(size_t newcap)
{
	resize(newcap, std::string());
}
void StrVec::resize(size_t newcap, const std::string &s)
{
	if (newcap >= size())
	{
		if (newcap >= capacity())reserve(newcap * 2);
		for (size_t i = size(); i != newcap; ++i)
			alloc.construct(first_free++, s);
	}
	else if (newcap < size())
	{
		while (first_free != elements + newcap)
			alloc.destroy(--first_free);
	}
}

int main()
{
	return 0;
}

13.40

StrVec::StrVec(std::initializer_list<std::string> lis)
{
	auto newdata = alloc_n_copy(lis.begin(), lis.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

13.41

因为顺序是先construct,后++,所以使用后置的++运算符。
如果使用前置的话,会导致first_free指向的第一个元素为未创建的。

13.42

#pragma once
#include<fstream>
#include<iostream>
#include<sstream>
//#include<vector>
#include<memory>
#include<string>
#include<map>
#include<set>
#include<algorithm>
#include"StrVec.h"
class QueryResult;
class TextQuery
{
public:
	using size_type = size_t;
	TextQuery(std::ifstream&);
	QueryResult query(const std::string&)const;
private:
	std::shared_ptr<StrVec> file; // 输入文件
	std::map<std::string, std::shared_ptr<std::set<size_type>>> wm; //每个单词与行号的map
};
class QueryResult
{
	friend std::ostream& print(std::ostream&, const QueryResult&);
public:
	QueryResult(std::string s, std::shared_ptr<std::set<TextQuery::size_type>> p,
		std::shared_ptr<StrVec> f) : sought(s), lines(p), file(f) { }
private:
	std::string sought;
	std::shared_ptr<std::set<TextQuery::size_type>> lines;
	std::shared_ptr<StrVec> file;
};

TextQuery::TextQuery(std::ifstream&ifs) :file(new StrVec)
{
	std::string text;
	while (getline(ifs, text))
	{
		file->push_back(text);
		int n = file->size() - 1;
		std::istringstream line(text);
		std::string text;
		while (line >> text)
		{
			std::string word;
			std::copy_if(text.begin(), text.end(), std::back_inserter(word), isalpha);
			auto &lines = wm[word];
			if (!lines)
				lines.reset(new std::set<size_type>);
			lines->insert(n);
		}
	}
}

QueryResult TextQuery::query(const std::string &sought) const
{
	static std::shared_ptr<std::set<TextQuery::size_type>> nodata(new std::set<TextQuery::size_type>);
	auto loc = wm.find(sought);
	if (loc == wm.end())
		return QueryResult(sought, nodata, file);
	else
		return QueryResult(sought, loc->second, file);
}

std::ostream &print(std::ostream &os, const QueryResult &qr)
{
	os << qr.sought << " occurs " << qr.lines->size() << " " /*<< make_plural(qr.lines->size(), "time", "s")*/ << std::endl;
	for (auto num : *qr.lines)
		os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << std::endl;
	return os;
}
// main函数可能要改一下
int main()
{
	return 0;
}

13.43

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

更倾向于前面的

13.44&47&48

头文件

#pragma once
#include<memory>
class newString
{
public:
	newString():elements(nullptr),end(nullptr){}
	newString(const char*);
	newString(const newString&);
	newString& operator=(const newString&);
	~newString();
	
	const char *c_str() const { return elements; }
	size_t size() { return end - elements; }
	size_t length() { return end - elements - 1; }

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

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

源文件

#include"newstring.h"
#include<iostream>
#include<algorithm>
#include<vector>
std::pair<char*, char*> newString::alloc_n_copy(const char*beg, const char*end)
{
	auto str = alloc.allocate(end - beg);
	return { str,std::uninitialized_copy(beg,end,str) };
}
void newString::range_initializer(const char*beg, const char*end)
{
	auto newstr = alloc_n_copy(beg, end);
	elements = newstr.first;
	end = newstr.second;
}
newString::newString(const char *s)
{
	char *sl = const_cast<char*>(s);
	while (*sl)
		++sl; //指向尾后
	range_initializer(s, ++sl);
}

newString::newString(const newString& rhs)
{
	range_initializer(rhs.elements, rhs.end);
	std::cout << "copy constructor" << std::endl;
}

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

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

newString& newString::operator = (const newString &rhs)
{
	auto newstr = alloc_n_copy(rhs.elements, rhs.end);
	free();
	elements = newstr.first;
	end = newstr.second;
	std::cout << "copy-assignment" << std::endl;
	return *this;
}


void foo(newString x)
{
	std::cout << x.c_str() << std::endl;
}

void bar(const newString& x)
{
	std::cout << x.c_str() << std::endl;
}

newString baz()
{
	newString ret("world");
	return ret;
}

int main()
{
	char text[] = "world";

	newString s0;
	newString s1("hello");
	newString s2(s0);
	newString s3 = s1;
	newString s4(text);
	s2 = s1;

	foo(s1);
	bar(s1);
	foo("temporary");
	bar("temporary");
	newString s5 = baz();

	std::vector<newString> svec;
	svec.reserve(8);
	svec.push_back(s0);
	svec.push_back(s1);
	svec.push_back(s2);
	svec.push_back(s3);
	svec.push_back(s4);
	svec.push_back(s5);
	svec.push_back(baz());
	svec.push_back("good job");

	for (const auto &s : svec) {
		std::cout << s.c_str() << std::endl;
	}
}

13.45

对于常规引用,成为左值引用。
绑定到右值的引用成为右值引用。

13.46

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

13.49

StrVec:

//移动构造函数
StrVec::StrVec(StrVec&&s) noexcept:
	elements(s.elements),first_free(s.first_free),cap(s.cap)
{
	//接管以后令右值指针都置空
	s.elements = s.first_free = s.cap = nullptr;
}
// 移动赋值运算符
StrVec& StrVec::operator=(StrVec&&rhs) noexcept
{
	//直接检测自赋值
	if (this != &rhs)
	{
		free(); //释放this
		elements = rhs.elements;
		first_free = rhs.first_free;
		cap = rhs.cap;
		rhs.elements = rhs.first_free = rhs.cap = nullptr;
	}
	return *this;
}

string:

//移动构造函数
newString::newString(newString&&s)noexcept :elements(s.elements),end(s.end)
{
	s.elements = s.end = nullptr;
}
newString& newString::operator=(newString&&rhs)noexcept
{
	if (this != &rhs)
	{
		free();
		elements = rhs.elements;
		end = rhs.end;
		rhs.elements = rhs.end = nullptr;
	}
	return *this;
}

Message

//移动构造函数
Message::Message(Message &&m) :contents(std::move(m.contents))
{
	move_Folders(&m);
}
//移动赋值运算符
Message& Message::operator=(Message&&rhs)
{
	if (this != &rhs) {
		remove_form_Folders();
		contents = std::move(rhs.contents);
		move_Folders(&rhs);
	}
	return *this;
}

13.50

String baz()
{
    String ret("world");
    return ret; // 1
}

String s5 = baz(); // 2

13.51

在这里是移动的操作而不是拷贝

13.52

左值被拷贝,右值被移动

13.53

不会

13.54

/*error: ambiguous overload for 'operator=' (operand types are 'HasPtr' and 'std::remove_reference<HasPtr&>::type { aka HasPtr }')
hp1 = std::move(*pH);
^*/

13.55

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

13.56

会产生递归造成溢出

13.57

没有问题,会使用移动版本的

13.58

#include <vector>
#include <iostream>
#include <algorithm>
class Foo {
public:
    Foo sorted() &&;
    Foo sorted() const &;
private:
    vector<int> data;
};
Foo Foo::sorted() && {
    sort(data.begin(), data.end());
    std::cout << "&&" << std::endl;
    return *this;
}
Foo Foo::sorted() const & {
    std::cout << "&" << std::endl;
    return Foo(*this).sorted(); 
}
int main()
{
    Foo().sorted(); 
    Foo f;
    f.sorted();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值