概述
999999999999999## C++primer第十三章(第五版)答案
13.1
拷贝构造函数定义了当用同类型的对象初始化本对象的时候做什么。如果构造函数的第一个参数是自身的引用,且所有参数都有默认值,则这个构造函数是一个拷贝构造函数。
拷贝构造函数发生在需要拷贝初始化的时候,拷贝初始化发生的条件如下
- 使用=定义变量时
- 将一个对象作为实参传递给一个非引用类型的形参
- 从一个返回类型为非引用类型的函数返回一个对象
- 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
13.2
拷贝构造函数的第一个参数必须是一个引用类型
13.3
拷贝StrBlob时,其成员shared_ptr的引用计数加一。
拷贝StrBlboPtr时,成员unique_ptr引用计数不变。
13.4
发生拷贝构造的条件 - 使用=定义变量时
- 将一个对象作为实参传递给一个非引用类型的形参
- 从一个返回类型为非引用类型的函数返回一个对象
- 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
Point global;
Point foo_bar(Point arg)//形参为非引用类型,调用拷贝构造
{
Point local = arg, *heap = new Point(global); //使用了“=”初始化对象,调用拷贝构造
*heap = local;
Point pa[4] = {local, *heap}; //使用了“=”初始化对象,调用拷贝构造
return *heap; //返回值为非引用类型,调用拷贝构造
}
13.5
#include <iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) :
ps(new string(s)), i(0) {};
HasPtr(const HasPtr& h) :ps(new string(*(h.ps))), i(h.i) {}
private:
string* ps;
int i;
};
13.6
-
拷贝赋值运算符是重载”=“运算符,即为一个名为operator=的函数,其参数与其所在类的的类型相同。
-
在发生赋值操作的时候使用。
-
合成拷贝赋值的工作:将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。
-
当类未定义自己的拷贝赋值运算符,编译器会生成一个合成拷贝运算符。
13.7
发生浅拷贝,赋值运算符两侧的指针指向同一块内存空间。shared_ptr引用计数加1,unique_ptr计数不变。
13.8
HasPtr& operator=(const HasPtr& has)
{
delete ps;
ps = new string(*has.ps);
i = has.i;
return *this;
}
13.9
-
析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数。
-
通常的,析构函数负责释放对象在生存期分配的所有资源。对于某些类,合成析构函数被用来阻止该类型的对象被销毁。如果不是这种情况,合成析构函数的函数体就为空。
-
当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。
13.10
所有对象的数据成员被销毁,智能指针的计数减一。
所有对象的数据成员被销毁,弱指针不影响计数器。
13.11
~HasPtr() {
delete ps;
}
13.12
三次,分别为item1,item2,accum。
当指向一个对象的引用或指针离开作用域时,析构函数不会执行。所以trans不会执行析构函数。
13.13
可以看到除了拷贝赋值运算符的不算,因为它并没有构造一个对象,构造函数和拷贝构造函数一共7个,而析构函数也有7个。其中引用形参传递没有调用拷贝构造函数,而非引用形参调用了拷贝构造函数,在离开了函数体时,对象调用析构函数,销毁对象。我们动态分配的内存空间需要亲自删除,如果没有删除,内存空间泄露,析构函数不会调用(去除最后的delete,析构函数会变为5个)。
#include <iostream>
#include<vector>
using namespace std;
struct X
{
int v;
X(int i):v(i) { cout << "构造函数" << endl; }
X(const X&) { cout << "拷贝构造函数" << endl; }
X& operator=(const X&) { cout << "拷贝复制运算符" << endl; return *this; }
~X() { cout << "析构函数" << endl; }
};
void reference(X&) { };
void unreference(X) { };
int main()
{
X x1(1);
X x2 = x1;
X x3(x2);
X * x4 = new X(2);
X* x5 = new X(x1);
x2 = x1;
vector<X>x6;
x6.push_back(x1);
cout << "引用传值 " << endl << endl;
reference(x1);
cout << "非引用传值" << endl;
unreference(x1);
cout << "程序结束" << endl;
delete x4,x5;
}
输出结果如下
构造函数
拷贝构造函数
拷贝构造函数
构造函数
拷贝构造函数
拷贝复制运算符
拷贝构造函数
引用传值
非引用传值
拷贝构造函数
析构函数
程序结束
析构函数
析构函数
析构函数
析构函数
析构函数
13.14
相同的内容
13.15
会改变,因为定义了一个拷贝构造函数,会生成新的序号。输出三个不同的序号。
13.16
如果有拷贝构造函数还是会生成三个不同的序号,因为b和c是通过拷贝初始化的。
13.17
#include <iostream>
#include<time.h>
using namespace std;
class numbered
{
public:
int mysn;
numbered() { mysn = rand() % 100000; };
/*numbered(numbered& a) {
mysn = rand() % 100000;
}*/
};
// void f(numbered s) {
// cout << s.mysn << endl;
// }
void f(const numbered& s) {
cout << s.mysn << endl;
}
int main() {
srand(time(0));
numbered a, b = a, c = b;
f(a); f(b); f(c);
return 0;
}
13.18
静态数据成员在类内定义,在类外声明。
#include <iostream>
using namespace std;
class Employee
{
Employee() = default;
Employee(const string& s)
private:
string name;
int idNum;
static int id ;
};
int Employee::id = 0;
Employee::Employee()
{
idNum = id++;
}
Employee::Employee(const string& s):name(s)
{
idNum = id++;
}
13.19
不需要,因为每个雇员都是唯一的。
#include <iostream>
using namespace std;
class Employee
{
Employee() = default;
Employee(const string& s);
Employee(const Employee&) = delete;
Employee& operator= (const Employee&) = delete;
private:
string name;
int idNum;
static int id ;
};
int Employee::id = 0;
Employee::Employee()
{
idNum = id++;
}
Employee::Employee(const string& s):name(s)
{
idNum = id++;
}
13.20
TextQuery和QueryResult的所有数据成员被拷贝,包括智能指针和容器,智能指针的引用计数加一。
析构智能指针引用计数减一,其他数据成员被销毁。
13.21
判断一个类是否需要定义他们自己版本的拷贝控制成员,可以看一个类是否需要自定义析构函数,如果一个类需要自定义析构函数,几乎可以肯定他也需要自定义拷贝控制函数成员。本类中的内存控制由智能指针自动控制,不需要定义析构函数,用合成默认析构函数即可。而且智能指针的拷贝不需要自定义拷贝构造函数,所以都设为=default即可。
13.22
编写一个赋值运算符时,一个好的模式是先将右侧的运算对象拷贝到一个局部临时对象中,当拷贝完成号,销毁左侧运算对象的现有成员就是安全的了。一旦左侧运算对象的资源被销毁,就只剩下将数据从临时对象拷贝到左侧运算对象的成员中了。
#include <iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) :
ps(new string(s)), i(0) {};
HasPtr(const HasPtr& h) :ps(new string(*(h.ps))), i(h.i) {}
HasPtr& operator=(const HasPtr& has)
{
auto new_ps = new string(*has.ps);
delete ps;
ps = new_ps;
i = has.i;
return *this;
}
~HasPtr() {
delete ps;
}
private:
string* ps;
int i;
};
13.23
第一次我就是直接释放本对象的内存空间。明显本节的代码安全性更强,在出现自我赋值时也可以正确工作。
13.24
未定义析构函数会导致内存泄漏。
未定义拷贝构造函数,会发生浅拷贝,也就是两个指针指向同一块区域,当发生析构时,会导致对同一块空间释放两次,也就是释放了非法内存。
13.25
拷贝构造函数和拷贝赋值运算符需要给对象分配一块新的内存空间。
不需要析构函数是因为智能指针能够自己控制内存释放,不需要delete。
13.26
StrBlob::StrBlob(const StrBlob& str)
{
data = make_shared<vector<string>>(*(str.data));//分配一块新的内存空间给对象,并用被拷贝对象的值初始化
}
StrBlob& StrBlob::operator=(const StrBlob&str)
{
data = make_shared<vector<string>>(*(str.data));//智能指针不需要亲自释放内存空间,=右侧的引用计数会减少,为零自动释放。
return*this;
}
13.27
#include <iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const string& s = string()) :
ps(new string(s)), i(0),use(new size_t(1)) {};
HasPtr(const HasPtr& has) :ps(has.ps), i(has.i),use(has.use)
{
++*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:
string* ps;
size_t* use;
int i;
};
13.28
和上题类似 代码原址
// .h文件
class TreeNode
{
public:
TreeNode () : value(std::string()), count (new int(1)), left(nullptr), right(nullptr) {}
TreeNode (const TreeNode& tn) : value (tn.value), count (tn.count), left (tn.left), right(tn.right) { ++ *count; }
TreeNode& operator= (const TreeNode& tn);
~ TreeNode()
{
if (0 == – *count) {
delete left;
delete right;
delete count;
}
}
private:
std::string value;
int *count;
TreeNode *left;
TreeNode *right;
};
class BinStrTree
{
public:
BinStrTree() : root (new TreeNode()) {}
BinStrTree(const BinStrTree& bst) : root (bst.root) {}
BinStrTree& operator= (const BinStrTree& bst);
~ BinStrTree () { delete root; }
private:
TreeNode *root;
};
// .cpp 文件
TreeNode& TreeNode::operator= (const TreeNode& tn)
{
++ *tn.count;
if (0 == *–count) {
delete left;
delete right;
delete count;
}
value = tn.value;
count = tn.count;
left = tn.left;
right = tn.right;
}
BinStrTree& BinStrTree::operator= (const BinStrTree& bst)
{
TreeNode *new_root = new TreeNode(*bst.root);
delete root;
root = new_root;
return *this;
}
13.29
因为swap函数里的swap为std的swap函数。
swap (lhs.ps, rhs.ps);调用了std::swap(string*, string*),swap (lhs.i, rhs.i);调用了std::swap(int, int)。调用了不同的swap函数,因此不会导致递归循环。
13.30
#include <iostream>
using namespace std;
class HasPtr
{
friend void swap(HasPtr&, HasPtr&);
public:
HasPtr(const string& s = string()) :
ps(new string(s)), i(0) {};
HasPtr(const HasPtr& h) :ps(new string(*(h.ps))), i(h.i) {}
HasPtr& operator=( HasPtr rhs)
{
this->swap(rhs);
return *this;
}
~HasPtr() {
delete ps;
}
string& getPs()
{
return *ps;
}
void swap(HasPtr& rhs)
{
using std::swap;
swap(ps, rhs.ps);
swap(i, rhs.i);
cout << "call the swap function." << endl;
}
private:
string* ps;
int i;
};
inline void swap(HasPtr& lhs, HasPtr& rhs)
{
lhs.swap(rhs);
}
int main()
{
HasPtr has1("hello");
HasPtr has2("world");
swap(has1, has2);
has1 = has2;
cout << has1.getPs() << endl;
cout << has2.getPs() << endl;
return 0;
}
13.30
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
class HasPtr
{
friend void swap(HasPtr&, HasPtr&);
friend bool operator<(const HasPtr& lhs, const HasPtr& rhs);
friend void show(const vector<HasPtr>& vec);
public:
HasPtr(const string& s = string()) :
ps(new string(s)), i(0) {};
HasPtr(const HasPtr& h) :ps(new string(*(h.ps))), i(h.i) {}
HasPtr& operator=( HasPtr has)
{
this->swap(has);
return*this;
}
~HasPtr() {
delete ps;
}
string& getPs()
{
return *ps;
}
void swap( HasPtr& rhs)
{
using std::swap;
swap(ps, rhs.ps);
swap(i, rhs.i);
cout << "call the swap function." << endl;
}
private:
string* ps;
int i;
};
inline void swap(HasPtr&lhs, HasPtr&rhs)
{
lhs.swap(rhs);
}
bool operator<(const HasPtr& lhs, const HasPtr& rhs)
{
cout << "call the operator< function" << endl;
return *lhs.ps < *rhs.ps;
}
void show(const vector<HasPtr>& vec)
{
auto beg = vec.begin();
while (beg != vec.end())
{
cout << *beg->ps << " ";
beg++;
}
cout << endl;
}
int main()
{
vector<HasPtr>vec;
HasPtr a("3");
HasPtr b("2");
HasPtr c("1");
vec.push_back(a);
vec.push_back(b);
vec.push_back(c);
show(vec);
sort(vec.begin(), vec.end());
show(vec);
}
13.32
类指针的版本,使用std::swap函数就可以实现指针交换,节省内存空间,不需要重新自定义swap函数。因此类指针的版本不会从swap函数收益。
13.33
因为我们需要更改文件夹f的值,所以不能是const,也不能该改的是他的拷贝。
13.34
class Message
{
friend class Folder;
friend void swap(Message& lhs, Message& rhs);
public:
explicit Message(const string& str = " ") :contents(str) { };
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
private:
string contents;
set< Folder*>folders;
void add_to_Floders(const Message&);
void remove_from_Folders();
};
Message::Message(const Message&mes):contents (mes.contents),folders(mes.folders)
{
add_to_Floders(mes);
}
Message& Message::operator=(const Message&mes)
{
remove_from_Folders();
contents = mes.contents;
folders = mes.folders;
add_to_Floders(mes);
return *this;
}
Message::~Message()
{
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_Floders(const Message&m)
{
for (auto f :m.folders)
{
f->addMsg(this);
}
}
void Message::remove_from_Folders()
{
for (auto f : folders)
{
f->remMsg(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);
swap(lhs.contents, rhs.contents);
for (auto f : lhs.folders)
{
f->addMsg(&lhs);
}
for (auto f : rhs.folders)
{
f->addMsg(&rhs);
}
}
13.35
如果使用合成的拷贝控制成员,folders不会同步更新,导致folders保存饿了错误的数据。
13.36
其中的构造函数,拷贝控制成员,析构函数,和Message类差不多。
class Folder
{
public:
Folder();
Folder(const Folder&);
Folder& operator= (const Folder&);
~Folder();
void addMsg(Message* m)
{
messages.insert(m);
}
void remMsg(Message* m)
{
messages.erase(m);
}
private:
set<Message*> messages;
};
13.37
void addFolder (Folder* f)
{
folders.insert(f);
}
void deleteFolder (Folder* f)
{
folders.erase(f);
}
13.38
对于分配了资源的类,定义swap可能是一种很重要的优化手段。但是Message类并没有动态分配内存,所以不使用swap没有什么关系。
13.39
#include <iostream>
#include<initializer_list>
#include<memory>
using namespace std;
class StrVec
{
public:
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(initializer_list<string>il)
{
auto data=alloc_n_copy(il.begin(), il.end());
elements = data.first;
first_free = cap = data.second;
}
StrVec(const StrVec&);
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; }
void reserve(size_t n);
void resize(size_t n, const string& s=" ");
private:
static allocator<string> alloc;
void chk_n_alloc()
{
if (size() == capacity())reallocate();
}
pair<string*, string*> alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string* elements;
string* first_free;
string* cap;
};
allocator<string> StrVec::alloc = allocator<string>();
pair<string*, string*> StrVec::alloc_n_copy(const string*b, const string*e)
{
auto data = alloc.allocate(e - b);
return pair<string*, string*>(data,uninitialized_copy(b,e,data));
}
void StrVec::free()
{
if (elements) {
auto b = first_free;
while (b!=elements)
{
alloc.destroy(--b);
}
alloc.deallocate(elements, cap - elements);
}
}
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++, move(*elem++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
StrVec::StrVec(const StrVec&s)
{
auto data=alloc_n_copy(s.begin(), s.end());
elements = data.first;
first_free = cap = data.second;
}
StrVec& StrVec::operator=(const StrVec&rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::push_back(const string&s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
StrVec::~StrVec()
{
free();
}
void StrVec::reserve(size_t n)
{
if (n < size())return;
auto newcapacity = n;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i < size(); i++)
{
alloc.construct(dest++, move(*elem++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
void StrVec::resize(size_t n, const string& s=" ")
{
if (n > size()) {
if (n > capacity()) {
reserve(n);
}
for (size_t i = size(); i < n; ++i) {
alloc.construct(first_free++, s);
}
}
else if (n < size()) {
while ((elements + n) != first_free) {
alloc.destroy(--first_free);
}
}
}
13.40
StrVec(initializer_list<string>il)
{
auto data=alloc_n_copy(il.begin(), il.end());
elements = data.first;
first_free = cap = data.second;
}
13.41
前置自增会先递增向后一个位置,此时指向的位置是第二个空位置,然后插入。会出现一个空位
13.42
为了方便我就全部放一起了,实际操作最好分开写
#include <vector>
#include<sstream>
#include<fstream>
#include<set>
#include<string>
#include<map>
#include<initializer_list>
#include<memory>
#include <iostream>
using namespace std;
class StrVec
{
public:
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(initializer_list<string>il)
{
auto data = alloc_n_copy(il.begin(), il.end());
elements = data.first;
first_free = cap = data.second;
}
StrVec(const StrVec&);
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; }
void reserve(size_t n);
void resize(size_t n, const string& s = " ");
private:
static allocator<string> alloc;
void chk_n_alloc()
{
if (size() == capacity())reallocate();
}
pair<string*, string*> alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string* elements;
string* first_free;
string* cap;
};
allocator<string> StrVec::alloc = allocator<string>();
pair<string*, string*> StrVec::alloc_n_copy(const string* b, const string* e)
{
auto data = alloc.allocate(e - b);
return pair<string*, string*>(data, uninitialized_copy(e, b, data));
}
void StrVec::free()
{
if (elements) {
auto b = first_free;
while (b != elements)
{
alloc.destroy(--b);
}
alloc.deallocate(elements, cap - elements);
}
}
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++, move(*elem++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
StrVec::StrVec(const StrVec& s)
{
auto data = alloc_n_copy(s.begin(), s.end());
elements = data.first;
first_free = cap = data.second;
}
StrVec& StrVec::operator=(const StrVec& rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
StrVec::~StrVec()
{
free();
}
void StrVec::reserve(size_t n)
{
if (n < size())return;
auto newcapacity = n;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i < size(); i++)
{
alloc.construct(dest++, move(*elem++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
void StrVec::resize(size_t n, const string& s )
{
if (n > size()) {
if (n > capacity()) {
reserve(n);
}
for (size_t i = size(); i < n; ++i) {
alloc.construct(first_free++, s);
}
}
else if (n < size()) {
while ((elements + n) != first_free) {
alloc.destroy(--first_free);
}
}
}
class QueryResult;
class TextQuery
{
friend class QueryResult;
shared_ptr <StrVec> file;
map<string, set<int>> lineNum;
public:
TextQuery(ifstream& input)
{
int num=0;
string line;
file = make_shared<StrVec>();
while (getline(input, line))
{
file->push_back(line);
istringstream s(line);
string temp;
while (s>>temp)
{
lineNum[temp].insert(num);
}
num++;
}
}
QueryResult query(const string& str);
};
class QueryResult
{
friend ostream& print(ostream& os,const QueryResult& q);
shared_ptr<StrVec> lines;
shared_ptr<set<int>>lineNum;
string word;
public:
QueryResult(TextQuery&t,const string&s)
{
word = s;
lineNum = make_shared<set<int>>(t.lineNum[s]);
lines = t.file;
}
};
ostream& print(ostream& os,const QueryResult& q)
{
if (q.lineNum->size() == 0)
{
cout << "Not found the word" ;
return os;
}
cout << q.word << "occurs" << q.lineNum->size() << (q.lineNum->size()>1?"times":"time") << endl;
for (auto i : *(q.lineNum))
{
os << "(Line number " << i << " ) " <<*(q.lines->begin()+i)<<endl;
}
return os;
}
QueryResult TextQuery::query(const string& str)
{
return QueryResult(*this, str);
}
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
while (true)
{
cout << "Enter word to look for,or q to quit:";
string s;
if (!(cin >> s) || s == "q")
break;
print(cout, tq.query(s)) << endl;
}
}
int main()
{
ifstream text("C:\Users\Kaji\Desktop\test.txt");
runQueries(text);
}
13.43
for_each更加简洁,但循环更加易读。
void StrVec::free()
{
if (elements) {
/* auto b = first_free;
while (b != elements)
{
alloc.destroy(--b);
}*/
for_each(elements, first_free, [](string& rhs) { alloc.destroy(&rhs); });
alloc.deallocate(elements, cap - elements);
}
}
13.44
#include <iostream>
#include<memory>
using namespace std;
class String
{
String() = default;
String(const char* c)
{
auto p= alloc_n_copy(c, c + sizeof(c));
beg = p.first;
end = p.second;
}
String(const String& s)
{
auto p = alloc_n_copy(s.beg, s.end);
beg = p.first;
end = p.second;
}
void free()
{
if (beg)
{
auto b = end;
while (b != beg)
alloc.destroy(--b);
}
alloc.deallocate(beg, end - beg);
}
private:
static allocator<char>alloc;
char* beg;
char* end;
pair<char*,char*>alloc_n_copy(const char*b,const char*e)
{
auto begin = alloc.allocate(e - b);
auto end = uninitialized_copy(b, e, begin);
return make_pair(begin,end);
}
};
13.45
左值持久,右值短暂,左值具有持久的状态,而右值是字面常量,要么是在表达式求值过程中创建的临时对象,也就是右值引用指向将要被销毁的对象,这意味着右值引用的代码可以自由地接管引用的对象的资源。
13.46
(1)函数返回值为临时对象,右值引用
(2)v[0]为变量是左值,左值引用
(3)r1为变量,左值引用
(4)表达式求值创建的临时对象,右值引用
13.47
其中push_back两次会调用3次拷贝构造函数,因为vector的capacity位置不够,会再次调用拷贝构造函数将旧元素拷贝至新内存。
#include <iostream>
#include<memory>
#include <vector>
using namespace std;
class String
{
public:
String() = default;
String(const char* c)
{
auto p= alloc_n_copy(c, c + sizeof(c));
beg = p.first;
end = p.second;
}
String(const String& s)
{
auto p = alloc_n_copy(s.beg, s.end);
beg = p.first;
end = p.second;
cout << "call the copy constructor function" << endl;
}
String& operator=(const String& rhs)
{
auto p = alloc_n_copy(rhs.beg, rhs.end);
free();
beg = p.first;
end = p.second;
cout << "call the operator= function" << endl;
}
void free()
{
if (beg)
{
auto b = end;
while (b != beg)
alloc.destroy(--b);
}
alloc.deallocate(beg, end - beg);
}
~String() { free(); }
private:
static allocator<char>alloc;
char* beg;
char* end;
pair<char*,char*>alloc_n_copy(const char*b,const char*e)
{
auto begin = alloc.allocate(e - b);
auto end = uninitialized_copy(b, e, begin);
return make_pair(begin,end);
}
};
allocator<char>String::alloc;
int main()
{
vector<String>Svec;
String a("hello");
String b = a;
Svec.push_back(a);
Svec.push_back(b);
}
13.48
见上题
13.49
String
String(String&& s)noexcept: beg (s.beg), end(s.end)
{
s.beg = s.end = nullptr;
cout << "call the move constructor function" << endl;
}
String& operator=( String&& rhs)noexcept
{
if (this != &rhs)
{
free();
beg = rhs.beg;
end = rhs.end;
rhs.beg = rhs.end = nullptr;
cout << "call the move operator= function" << endl;
}
StrVec
StrVec(StrVec&& vec)noexcept:elements(vec.elements),first_free(vec.first_free),cap(vec.cap)
{
vec.elements = vec.first_free = vec.cap = nullptr;
}
StrVec& operator=(StrVec&& rhs)noexcept
{
if(this!=&rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
Message
void moveFolders(Message* m)
{
folders = std::move(m->folders);
for (auto f : folders) {
f->remMsg(m);
f->addMsg(this);
}
m->folders.clear();
}
Message(Message&& m) :contents(std::move(m.contents))
{
moveFolders(&m);
}
Message& operator=( Message&&rhs)
{
if (this != &rhs)
{
remove_from_Folders();
contents = std::move(rhs.contents);
moveFolders(&rhs);
}
return *this;
}
13.50
在vector需要扩充内存空间时,直接使用移动构造函数,将旧内存空间元素的控制权交由新的内存空间。
13.51
不能拷贝unique_ptr的规则有一个例外,我们可以拷贝一格或赋值一个将要被销毁的unique_ptr,因为编译器知道对象将被销毁,所以执行一个特殊的拷贝,移动拷贝。
13.52
- hp=hp2,会调用赋值运算符,由于赋值运算符的参数为非引用参数,所以在该参数需要初始化,hp2为左值,所以会调用拷贝构造函数。拷贝构造函数会给该参数一个新的内存空间,并用hp2的值初始化。然后通过swap函数,与该临时对象交换数据成员,函数结束退出,临时对象被销毁。
- hp=std::move(hp2),和前面一样,赋值运算符的参数需要初始化,但由于使用了move函数,将一个右值引用绑定到hp2上,所以该参数会精确匹配移动构造函数,该临时对象获得hp2的指针,并不分配内存空间,然后与hp交换数据成员,函数结束,临时对象销毁。
13.53
因为每次调用赋值运算符都会创建一个临时对象存放变量。
13.54
会出现运算符=不明确。
13.55
void push_back(string&& t) { data->push_back(std::move(t)); }
13.56
因为ret是一个左值,因此调用ret.sorted()时不会匹配到Foo sorted() &&右值版本,而是继续调用自身Foo sorted() const&,因此程序会陷入无限递归中,并且不停复制this,造成栈内存泄漏。
13.57
因为Foo(*this)返回的是一个右值,因此可以调用sorted的右值版本,返回排序后的Foo。
13.58
#include <vector>
#include <iostream>
#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 << "&&" <<endl;
return *this;
}
Foo Foo::sorted() const&
{
cout << "const &" << std::endl;
return Foo(*this).sorted();
}
int main()
{
Foo().sorted();
Foo f;
f.sorted();
}
最后
以上就是雪白星星为你收集整理的C++primer第十三章(第五版)答案的全部内容,希望文章能够帮你解决C++primer第十三章(第五版)答案所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复