本文答案,部分参考于C++ Primer 习题集
前面章节的习题答案
配套的学习资料
https://www.jianguoyun.com/p/DTK5uJgQldv8CBjKv80D
13.1
如果构造函数的第一个参数是自身类类型的引用.且所有其他参数都有默认值.则此构造函数是拷贝构造函数,拷贝构造函数在以下几种情况下会被使用.
拷贝初始化(用=定义变量)
将一个对象作为实参传递给非引用类型的形参
一个返回类型为非引用类型的函数返回一个对象.
用花括号列表初始化一个数组中的元素或一个聚合类中的成员.
初始化标准库容器或调用其insert/push操作时,容器会对其元素进行拷贝初始化.
13.2
是非法的.
这样调用会造成死循环.
13.3
这两个类都为定义拷贝构造函数.因此编译器为它们定义了合成的拷贝构造函数.合成的拷贝构造函数逐个拷贝非const成员.对内置类型的成员.直接进行内存拷贝.
因此.拷贝一个StrBlob时,拷贝其唯一的成员data.使用shared_ptr的拷贝构造函数来进行拷贝.因此其引用计数增加1
拷贝一个StrBlobPtr时,拷贝成员wptr.用weak_ptr的拷贝构造函数进行拷贝.引用计数不变.然后拷贝curr.直接进行内存复制.
13.4
1 local =arg 将arg拷贝给local
2 *head=local 将local拷贝到heap指定的地址中.
3 Point pa[4]={local,heap} 将local 和
*heap
拷贝给数组的前两个元素.
4 return *heap
13.5
HasPtr::HasPtr(const HasPtr& hp) {
ps = new string(*hp.ps);
i = hp.i;
}
13.6
拷贝赋值运算符本身是一个重载的赋值运算符,定义为类的成员函数,左侧运算对象绑定到隐含的this参数.而右侧运算对象是所属类类型的.作为函数的参数,函数返回指向其左侧运算对象的引用.
当对类对象进行赋值时,会使用拷贝赋值运算符.
通常情况下.合成的拷贝赋值运算符会将右侧对象的非static成员逐个赋予左侧对象的对应成员.这些赋值操作是由成员类型的拷贝赋值运算符完成的.
若一个类未定义自己的拷贝赋值运算符,编译器就会为其合成拷贝赋值运算符.完成赋值操作.但对于某些类.还会起到禁止该类型对象赋值的效果.
13.7
因为我们在这两个类都没有定义拷贝赋值运算符.
所以它们赋值的时候,用的是编译器给的合成的拷贝复制运算符.
赋值data的时候,使用的是shared_ptr的拷贝赋值运算符.
引用计数增加1.
赋值StrBlobPtr 时,因为weak_ptr是弱引用.引用计数是不会变的.
13.8
class HasPtr {
public:
HasPtr(const std::string& s = std::string()) :ps(new std::string(s)),i(0) {}
private:
std::string* ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr& rhs) {
auto news = new string(*rhs.ps);
delete ps;
ps = news;
i = rhs.i;
return *this;
}
13.9
析构函数完成与构造函数相反的工作:释放对象使用的资源.销毁非静态数据成员.从语法上看.它是类的一个成员函数.名字是波浪号按类名.没有返回值.也不接受参数.
当一个参数没有定义析构函数时,编译器会为它合成析构函数.
合成的析构函数体为空.但这并不意味着它什么也不做.当空函数体执行完毕后.非静态数据成员非被逐个销毁.成员是在析构函数体之后的隐含的析构阶段中进行销毁的.
13.10
Strblob中就一个data可以销毁,data还是shared_ptr类型的.在销毁的时候,data的引用计数器会减一,如果减到了0.就会被销毁.如果没有减到0.就不会.
Strblobk就直接销毁.因为wptr是弱引用的智能指针.
13.11
~HasPtr() { delete ps; }
13.12
三次
item1 item2 accum
13.13
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class HasPtr {
public:
HasPtr(const std::string& s = std::string()) :ps(new std::string(s)),i(0) {}
~HasPtr() { delete ps; }
private:
std::string* ps;
int i;
};
struct X {
X() { cout << "X()" << endl; }
X(const X&) { cout << "X(const X&)" << endl; }
X& operator=(const X& rhs) { cout << "拷贝赋值运算符=(const X&)" << endl; return *this; }
~X() { cout << "析构函数 ~X()" << endl; }
};
void f1(X x) {}
void f2(X& x) {}
int main(void) {
cout << "局部变量" << endl;
X x;
cout << endl;
cout << "非引用参数传递" << endl;
f1(x);
cout << endl;
cout << "引用参数传递" << endl;
f2(x);
cout << endl;
cout << "动态分配" << endl;
X* px = new X;
cout << endl;
cout << "添加到容器中" << endl;
vector<X> vx;
vx.push_back(x);
cout << endl;
cout << "释放动态分配对象" << endl;
delete px;
cout << endl;
cout << "间接初始化和赋值" << endl;
X y = x;
y = x;
cout << endl;
cout << "程序结束:" << endl;
return 0;
}
13.14
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int index = 0;
class numbered {
public:
numbered(){ mysn = index++; }
int mysn;
};
void f(numbered s) { cout << s.mysn << endl; }
int main(void) {
numbered a, b = a, c = b;
f(a); f(b); f(c);
return 0;
}
输出的结果如下:
0
0
0
13.15
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int index = 0;
class numbered {
public:
numbered(){ mysn = index++; }
numbered(numbered& n) { mysn = index++; };
int mysn;
};
void f(numbered s) { cout << s.mysn << endl; }
int main(void) {
numbered a, b = a, c = b; //这个时候是0,1,2
f(a); f(b); f(c); //这个时候是3,4,5 输出的是numbered s的临时值
cout << index << endl;
cout << a.mysn << ":" << b.mysn << ":" << c.mysn << endl;
return 0;
}
结果如下:
3
4
5
6
0:1:2
13.16
会改变.因为这样numbered s的值就是不变的了.不会调用拷贝构造函数
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int index = 0;
class numbered {
public:
numbered(){ mysn = index++; }
numbered(numbered& n) { mysn = index++; };
int mysn;
};
void f(const numbered &s) { cout << s.mysn << endl; }
int main(void) {
numbered a, b = a, c = b; //这个时候是0,1,2
f(a); f(b); f(c); //这个时候是3,4,5 输出的是numbered s的临时值
cout << index << endl;
cout << a.mysn << ":" << b.mysn << ":" << c.mysn << endl;
return 0;
}
输入输出如下:
0
1
2
3
0:1:2
13.17
前面的汇总
13.18
#include<iostream>
#include<string>
#include<vector>
using namespace std;
static int index = 0;
class Employee {
public:
Employee(string a) { name = a; id = index++; }
void Prt() { cout << name << ":" << id << endl; }
private:
string name;
int id;
};
int main(void) {
Employee a("Link");
a.Prt();
Employee b("Zelda");
b.Prt();
return 0;
}
13.19
需要,因为默认的会出现序号相同的情况.
#include<iostream>
#include<string>
#include<vector>
using namespace std;
static int index = 0;
class Employee {
public:
Employee(string a) { name = a; id = index++; }
Employee(const Employee& c) { name = c.name; id = index++; }
Employee& operator=(Employee& rhs) { name = rhs.name; return *this; }
void Prt() { cout << name << ":" << id << endl; }
private:
string name;
int id;
};
int main(void) {
Employee a("Link");
a.Prt();
Employee b("Zelda");
b.Prt();
Employee c(a);
Employee d = b;
c.Prt();
d.Prt();
return 0;
}
13.20
两个类都未定义拷贝控制成员,因此都是编译器为它们定义合成版本.
当TextQuery销毁时.合成版本会销毁其file和wm成员.对file成员.会将shared_ptr的引用计数减一.若变为0,则销毁所管理的动态vector对象.(会调用vector和string的析构函数).对wm.调用map的析构函数.(从而调用string,shared_ptr和set的析构函数) 会正确释放资源.
当QueryResult销毁释.合成版本会销毁其sought.lines和files成员.类似TextQuery .String,shared_ptr.set.vector的析构函数可能被调用.因为这些类都有设计良好的拷贝控制成员.会正确释放资源.
当拷贝一个TextQuery时,合成版本会拷贝file和wm成员.对file.shared_ptr的引用计数会加1.对wm.会调用map的拷贝构造函数(继而调用string,shared_ptr)和set的拷贝构造函数.因此会正确进行拷贝操作.赋值操作类似.只不过会将原来的资源释放掉.例如.原有的file的引用计数会减一.
QueryResult的拷贝和赋值类似
13.21
两个类都未定义拷贝控制成员.但是它们同智能指针管理共享的动态对象(输入文件内容.查询结果的行号集合) 用标准库容器保存大量容器.而这些标准库机制都有都有设计良好的拷贝控制成员.用合成的拷贝控制成员简单地拷贝.赋值.销毁它们.即可保证正确的资源管理.因此.这两个类并不需要定义自己的拷贝控制成员.实际上,这两个类的类对象之间就存在资源共享.目前的设计已能很好地实现这种共享.同类对象之间的共享也自然能够解决.
13.22
#include<iostream>
#include<string>
using namespace std;
class HasPtr {
public:
HasPtr(const string& s = string()) :ps(new string(s)), i(0) {}
HasPtr(const HasPtr& p) :ps(new string(*p.ps)), i(p.i) {} //拷贝构造函数
HasPtr& operator=(const HasPtr&); //拷贝赋值运算符
HasPtr& operator=(const string&); //赋予新string
string& operator*(); //解引用
~HasPtr();
private:
string* ps;
int i;
};
HasPtr::~HasPtr() {
delete ps; //释放string内存
}
inline HasPtr& HasPtr::operator=(const HasPtr& rhs) {
auto newps = new string(*rhs.ps); //拷贝指针指向的对象
delete ps; //销毁原string
ps = newps; //指向新string
i = rhs.i; //使用内置的int赋值
return *this; //返回一个此对象的引用.
}
HasPtr& HasPtr::operator=(const string& rhs) {
*ps = rhs;
return *this;
}
string& HasPtr::operator*() {
return *ps;
}
int main(void) {
HasPtr h("hi mom!");
HasPtr h2(h);
HasPtr h3 = h;
h2 = "hi dad!";
h3 = "hi son!";
cout << "h:" << *h << endl;
cout << "h2:" << *h2 << endl;
cout << "h3:" << *h3 << endl;
return 0;
}
13.23
略
13.24
如果未定义析构函数,在销毁HasPtr对象时合成的析构函数不会释放指针ps指向的内存.造成内存泄漏.
如果未定义拷贝构造函数,在拷贝HasPtr对象时,合成的拷贝构造函数会简单赋值ps成员.使得两个HasPtr指向相同的string.当其中一个HasPtr修改string内容时,另一个HasPtr也被改变.这并不符合我们的设想.如果同时定义了析构函数.情况会更为糟糕.当销毁其中一个HasPtr时,Ps指向的String被销毁.另一个HasPtr的Ps成为空悬指针.
13.25
由于希望StrBlob的行为像值一样.因此在拷贝构造函数和拷贝赋值运算符中.我们应该将其数据-string的vector拷贝一份,使得两个StrBlob对象指向各自的数据,而不是简单的拷贝shared_ptr使得两个StrBlob指向同一个vector.
Strblob不需要析构函数的原因是,它管理的全部资源就是string的vector.而这是有shared_ptr负责管理的.当一个StrBlob对象销毁时,会调用shared_ptr的析构函数.它会正确调整引用计数.当需要(引用计数变为0)释放vector.即.shared_ptr保证了资源分配.释放的正确性.StrBlob就不必进行相应的处理了.
13.26
13.27
13.28
TreeNode() :value(""), count(1), left(nullptr), right(nullptr) {}
TreeNode(const TreeNode& tn) :value(tn.value), count(1), left(tn.left), right(tn.right) {
}
BinStrTree::BinStrTree(const BinStrTree &bst): root(bst.root){
root->CopyTree();
}
13.29
在此swap函数中又调用了swap来交换HasPtr成员中的ps和i.但这两个成员的类型分别是指针和整型.都是内置类型.因此函数中的swap调用被解析为std::swap,而不是HasPtr的特定版本swap(也就是自身).所以不会导致递归循环.
13.30
inline void swap(HasPtr& lhs, HasPtr& rhs) {
cout << "交换" << *lhs.ps << "和" << *rhs.ps << endl;
swap(lhs.ps, rhs.ps);
swap(lhs.i, rhs.i);
}
13.31
bool operator<(const HasPtr&) const;
bool HasPtr::operator<(const HasPtr& rhs) const {
return *ps < *rhs.ps;
}
13.32
默认swap版本简单交换两个对象的非静态成员.对HasPtr而言.就是交换string指针ps,引用计数指针use 和整型值i.可以看出.这种语义是符合期望的–两个HasPtr指向了原来对方的string。而两者互换string后,各自的引用计数应该是不变的.(都是减一再加1).因此默认swap版本已经能正确处理指针HasPtr的交换.专用swap版本不会带来更多收益.
13.33
首先,我们需要将给定的Folder的指针添加到当前Message的Folder集合中.这样.参数类型就不能是Folder.必须是引用类型.否则,调用save时会将实参拷贝给形参.folders.insert添加的形参(与局部变量一样放在栈中.save执行完毕就被销毁)的指针.而非原始Folder的指针.而参数为引用类型.就可以令形参与实参指向相同的对象.对形参f取地址.得到的就是原始Folder(实参) 的指针.
其次.我们需要调用addMsg将当前Message的指针添加到Folder的message集合中.我们修改了Folder的内容.因此参数不能是const的.
13.34
参考书中本节内容完成Message类的编写
Folder.h
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
* copyright and warranty notices given in that book:
*
* "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced.Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
#ifndef FOLDER_H
#define FOLDER_H
#include "Version_test.h"
#include <string>
#include <set>
class Folder;
class Message {
friend void swap(Message&, Message&);
friend class Folder;
public:
// folders is implicitly initialized to the empty set
explicit Message(const std::string &str = ""):
contents(str) { }
// copy control to manage pointers to this Message
Message(const Message&); // copy constructor
Message& operator=(const Message&); // copy assignment
~Message(); // destructor
Message(Message&&); // move constructor
Message& operator=(Message&&); // move assignment
// add/remove this Message from the specified Folder's set of messages
void save(Folder&);
void remove(Folder&);
void debug_print(); // print contents and it's list of Folders,
// printing each Folder as well
private:
std::string contents; // actual message text
std::set<Folder*> folders; // Folders that have this Message
// utility functions used by copy constructor, assignment, and destructor
// add this Message to the Folders that point to the parameter
void add_to_Folders(const Message&);
void move_Folders(Message*);
// remove this Message from every Folder in folders
void remove_from_Folders();
// used by Folder class to add self to this Message's set of Folder's
void addFldr(Folder *f) { folders.insert(f); }
void remFldr(Folder *f) { folders.erase(f); }
};
// declaration for swap should be in the same header as Message itself
void swap(Message&, Message&);
class Folder {
friend void swap(Message&, Message&);
friend class Message;
public:
~Folder(); // remove self from Messages in msgs
Folder(const Folder&); // add new folder to each Message in msgs
Folder& operator=(const Folder&); // delete Folder from lhs messages
// add Folder to rhs messages
Folder(Folder&&); // move Messages to this Folder
Folder& operator=(Folder&&); // delete Folder from lhs messages
// add Folder to rhs messages
#ifdef DEFAULT_FCNS
Folder() = default; // defaults ok
#else
Folder() { } // msgs will be default initialized, which is what we want
#endif
void save(Message&); // add this message to folder
void remove(Message&); // remove this message from this folder
void debug_print(); // print contents and it's list of Folders,
private:
std::set<Message*> msgs; // messages in this folder
void add_to_Messages(const Folder&);// add this Folder to each Message
void remove_from_Msgs(); // remove this Folder from each Message
void addMsg(Message *m) { msgs.insert(m); }
void remMsg(Message *m) { msgs.erase(m); }
void move_Messages(Folder*); // move Message pointers to point to this Folder
};
#endif
13.35
Message 类包含两个数据成员.content为string类型.folder为set.这两个标准库类都又完备的拷贝控制成员.因此Message使用合成的拷贝控制成员的话.简单拷贝这两个成员也能实现正确拷贝.
当拷贝Message时,不仅要拷贝这个Message 在那些Folder中.还要将Message加到每个Folder中.调用addMsg
当销毁Message时,需要将它从所有Folder中删除-调用remMsg。
因此.不能依赖合成的拷贝控制成员.必须设计自己的版本来完成这些工作
13.36
见13.34
13.37
例如:
void remFldr(Folder *f){floders.erase(f);}
13.38
1 由于赋值运算符的参数是Message类型.因此会将实参拷贝给形参rhs.这会触发拷贝构造函数,将实参的contents和folders拷贝给rhs.并调用add_to_Folders将rhs添加到folders的所有文件夹中.
2 随后赋值运算符调用swap交换 *this和rhs.首先遍历两者的foldrers.将它们从自己的文件夹中删除.然后调用string 和set的swap交换它们的contents和folders.最后在遍历两者新的folders。将它们分别添加到自己的新文件夹中.
3 最好.赋值运算符结束.rhs被销毁.析构函数调用remove_from_Folders将rhs从自己的所有文件夹中删除.
显然.语义是正确的.达到了预期目的.但是效率低下.
13.39+13.40
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
* copyright and warranty notices given in that book:
*
* "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced.Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
#ifndef STRVEC_H
#define STRVEC_H
#include "Version_test.h"
#include <iostream>
#include <memory>
#include <utility>
#ifdef INIT_LIST
#include <initializer_list>
#endif
// simplified implementation of the memory allocation strategy for a vector-like class
class StrVec {
public:
// copy control members
StrVec():
elements(nullptr), first_free(nullptr), cap(nullptr) { }
StrVec(const StrVec&); // copy constructor
StrVec &operator=(const StrVec&); // copy assignment
#ifdef NOEXCEPT
StrVec(StrVec&&) noexcept; // move constructor
StrVec &operator=(StrVec&&) noexcept; // move assignment
~StrVec() noexcept; // destructor
#else
StrVec(StrVec&&) throw(); // move constructor
StrVec &operator=(StrVec&&) throw(); // move assignment
~StrVec() throw(); // destructor
#endif
#ifdef INIT_LIST
// additional constructor
StrVec(std::initializer_list<std::string>);
#else // define a constructor that takes pointers to a range of elements
StrVec(const std::string*, const std::string*);
#endif
void push_back(const std::string&); // copy the element
void push_back(std::string&&); // move the element
// add elements
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
// iterator interface
std::string *begin() const { return elements; }
std::string *end() const { return first_free; }
#ifdef INIT_LIST // no real substitute for initializer_list in assignments
// operator functions covered in chapter 14
StrVec &operator=(std::initializer_list<std::string>);
#endif
std::string& operator[](std::size_t n)
{ return elements[n]; }
const std::string& operator[](std::size_t n) const
{ return elements[n]; }
#ifdef VARIADICS // no direct substitute for variadic functions
// emplace member covered in chapter 16
template <class... Args> void emplace_back(Args&&...);
#endif
private:
static std::allocator<std::string> alloc; // allocates the elements
// utility functions:
// used by members that add elements to the StrVec
void chk_n_alloc()
{ if (size() == capacity()) reallocate(); }
// used by the copy constructor, assignment operator, and destructor
std::pair<std::string*, std::string*> alloc_n_copy
(const std::string*, const std::string*);
void free(); // destroy the elements and free the space
void reallocate(); // get more space and copy the existing elements
std::string *elements; // pointer to the first element in the array
std::string *first_free; // pointer to the first free element in the array
std::string *cap; // pointer to one past the end of the array
};
#include <algorithm>
inline
#ifdef NOEXCEPT
StrVec::~StrVec() noexcept { free(); }
#else
StrVec::~StrVec() throw() { free(); }
#endif
inline
std::pair<std::string*, std::string*>
StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
// allocate space to hold as many elements as are in the range
auto data = alloc.allocate(e - b);
// initialize and return a pair constructed from data and
// the value returned by uninitialized_copy
#ifdef LIST_INIT
return {data, uninitialized_copy(b, e, data)};
#else
return make_pair(data, uninitialized_copy(b, e, data));
#endif
}
inline
#ifdef NOEXCEPT
StrVec::StrVec(StrVec &&s) noexcept // move won't throw any exceptions
#else
StrVec::StrVec(StrVec &&s) throw() // move won't throw any exceptions
#endif
// member initializers take over the resources in s
: elements(s.elements), first_free(s.first_free), cap(s.cap)
{
// leave s in a state in which it is safe to run the destructor
s.elements = s.first_free = s.cap = nullptr;
}
inline
StrVec::StrVec(const StrVec &s)
{
// call alloc_n_copy to allocate exactly as many elements as in s
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
inline
void StrVec::free()
{
// may not pass deallocate a 0 pointer; if elements is 0, there's no work to do
if (elements) {
// destroy the old elements in reverse order
for (auto p = first_free; p != elements; /* empty */)
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
}
#ifdef INIT_LIST
inline
StrVec &StrVec::operator=(std::initializer_list<std::string> il)
{
// alloc_n_copy allocates space and copies elements from the given range
auto data = alloc_n_copy(il.begin(), il.end());
free(); // destroy the elements in this object and free the space
elements = data.first; // update data members to point to the new space
first_free = cap = data.second;
return *this;
}
#endif
inline
#ifdef NOEXCEPT
StrVec &StrVec::operator=(StrVec &&rhs) noexcept
#else
StrVec &StrVec::operator=(StrVec &&rhs) throw()
#endif
{
// direct test for self-assignment
if (this != &rhs) {
free(); // free existing elements
elements = rhs.elements; // take over resources from rhs
first_free = rhs.first_free;
cap = rhs.cap;
// leave rhs in a destructible state
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
inline
StrVec &StrVec::operator=(const StrVec &rhs)
{
// call alloc_n_copy to allocate exactly as many elements as in rhs
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
inline
void StrVec::reallocate()
{
// we'll allocate space for twice as many elements as the current size
auto newcapacity = size() ? 2 * size() : 1;
// allocate new memory
auto newdata = alloc.allocate(newcapacity);
// move the data from the old memory to the new
auto dest = newdata; // points to the next free position in the new array
auto elem = elements; // points to the next element in the old array
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free(); // free the old space once we've moved the elements
// update our data structure to point to the new elements
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
#ifdef INIT_LIST
inline
StrVec::StrVec(std::initializer_list<std::string> il)
{
// call alloc_n_copy to allocate exactly as many elements as in il
auto newdata = alloc_n_copy(il.begin(), il.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
#else
inline
StrVec::StrVec(const std::string *b, const std::string* e)
{
// call alloc_n_copy to allocate exactly as many elements as in the range
auto newdata = alloc_n_copy(b, e);
elements = newdata.first;
first_free = cap = newdata.second;
}
#endif
inline
void StrVec::push_back(const std::string& s)
{
chk_n_alloc(); // ensure that there is room for another element
// construct a copy of s in the element to which first_free points
alloc.construct(first_free++, s);
}
inline
void StrVec::push_back(std::string &&s)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::move(s));
}
#ifdef VARIADICS // no direct substitute for variadic functions
// emplace member covered in chapter 16
template <class... Args>
inline
void StrVec::emplace_back(Args&&... args)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::forward<Args>(args)...);
}
#endif
#endif
13.41
因为first_free指向第一个空闲的位置,也就是最后一个string的尾后位置.当添加新的string时,应该保存在first_free指向的位置.然后将first_free推进一个位置.因此后置递增运算恰好符合要求.
如果使用前置递增运算.则是先将first_free推进一个位置.然后将新string保存在新位置上.显然,这种方法意味着first_free指向最后一个string.而非尾后位置.与first_free的设定不吻合.
13.42
1 TextQuery.h中file成员的定义改为:
std::shared_ptr<StrVec> file;
2 QueryResult.h中file成员的定义改为
std::shared_ptr<StrVec> file;
构造函数的第三个参数改为:
std::shared_ptr<StrVec> f
成员函数get_file的定义改为:
std::shared_ptr<StrVec> get_file(){return file;}
3 TextQuery.cpp中构造函数初始化file成员的操作改为:
TextQuery::TextQuery(ifstream &is):file(new StrVec)
13.43
将for循环改成如下形式即可:
elements和first_free是string*类型,因此它们指出的范围中的元素是string类型.因此.lambda的参数s应该是string&类型,在lambda的函数体中应该去s的地址.用来调用destory.
for_each(elements,first_free,[](std::string &s){alloc.destory(&s);});
13.44
总体上需要注意的地方与StrVec的定义类似,只不过StrVec管理的元素为string。而String管理的元素为char 配套网站已有完整程序.读者可以尝试自己定义String.然后与配套网站中的代码进行对照.
13.45
所谓右值引用就是必须绑定到右值的引用.通过&&获得.右值引用只能绑定到一个将要销毁的对象上.因此可以自由地移动其资源.
左值引用.也就是"常规引用",不能绑定到要转换的表达式.字面常量或返回右值的表达式.而右值引用恰好相反.可以绑定到这类表达式.但不能绑定到一个左值上.
返回左值的表达式包括返回左值引用的函数及赋值.下标.解引用和前置递增.递减运算符.返回右值的包括返回非引用类型的函数及算术.关系.位和后置递增.递减运算符.可以看到.左值的特点是有持久的状态.而右值则是短暂的.
13.46
&
&&
&
&&
13.47
加几条cout<<endl的事
13.48
只有前两个.
#pragma once
/*
* This file contains code from "C++ Primer, Fifth Edition", by Stanley B.
* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the
* copyright and warranty notices given in that book:
*
* "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."
*
*
* "The authors and publisher have taken care in the preparation of this book,
* but make no expressed or implied warranty of any kind and assume no
* responsibility for errors or omissions. No liability is assumed for
* incidental or consequential damages in connection with or arising out of the
* use of the information or programs contained herein."
*
* Permission is granted for this code to be used for educational purposes in
* association with the book, given proper citation if and when posted or
* reproduced.Any commercial use of this code requires the explicit written
* permission of the publisher, Addison-Wesley Professional, a division of
* Pearson Education, Inc. Send your request for permission, stating clearly
* what code you would like to use, and in what specific way, to the following
* address:
*
* Pearson Education, Inc.
* Rights and Permissions Department
* One Lake Street
* Upper Saddle River, NJ 07458
* Fax: (201) 236-3290
*/
#ifndef STRING_H
#define STRING_H
#include "Version_test.h"
#include <cstring>
#include <algorithm>
#include <cstddef>
#include <iostream>
#ifdef INITIALIZER_LIST
#include <initializer_list>
#endif
#include <iostream>
#include <memory>
class String {
friend String operator+(const String&, const String&);
friend String add(const String&, const String&);
friend std::ostream& operator<<(std::ostream&, const String&);
friend std::ostream& print(std::ostream&, const String&);
public:
#if defined(IN_CLASS_INITS) && defined(DEFAULT_FCNS)
String() = default;
#else
String() : sz(0), p(nullptr) { }
#endif
// cp points to a null terminated array,
// allocate new memory & copy the array
String(const char* cp) :
sz(std::strlen(cp)), p(a.allocate(sz))
{
std::uninitialized_copy(cp, cp + sz, p);
}
// copy constructor: allocate a new copy of the characters in s
String(const String& s) :sz(s.sz), p(a.allocate(s.sz))
{
std::uninitialized_copy(s.p, s.p + sz, p);
}
// move constructor: copy the pointer, not the characters,
// no memory allocation or deallocation
#ifdef NOEXCEPT
String(String&& s) noexcept : sz(s.size()), p(s.p)
#else
String(String&& s) throw() : sz(s.size()), p(s.p)
#endif
{
s.p = 0; s.sz = 0;
}
String(size_t n, char c) : sz(n), p(a.allocate(n))
{
std::uninitialized_fill_n(p, sz, c);
}
// allocates a new copy of the data in the right-hand operand;
// deletes the memory used by the left-hand operand
String& operator=(const String&);
// moves pointers from right- to left-hand operand
#ifdef NOEXCEPT
String& operator=(String&&) noexcept;
#else
String& operator=(String&&) throw();
#endif
// unconditionally delete the memory because each String has its own memory
#ifdef NOEXCEPT
~String() noexcept { if (p) a.deallocate(p, sz); }
#else
~String() throw() { if (p) a.deallocate(p, sz); }
#endif
// additional assignment operators
String& operator=(const char*); // car = "Studebaker"
String& operator=(char); // model = 'T'
#ifdef INITIALIZER_LIST
String&
operator=(std::initializer_list<char>); // car = {'a', '4'}
#endif
const char* begin() { return p; }
const char* begin() const { return p; }
const char* end() { return p + sz; }
const char* end() const { return p + sz; }
size_t size() const { return sz; }
void swap(String& s)
{
auto tmp = p; p = s.p; s.p = tmp;
auto cnt = sz; sz = s.sz; s.sz = cnt;
}
private:
#ifdef IN_CLASS_INITS
std::size_t sz = 0;
char* p = nullptr;
#else
std::size_t sz;
char* p;
#endif
static std::allocator<char> a;
};
String make_plural(size_t ctr, const String&, const String&);
inline
void swap(String& s1, String& s2)
{
s1.swap(s2);
}
#endif
13.49
String的如下:
String& String::operator=(String&& rhs) noexcept {
if (this != &rhs) {
if (p)
a.deallocate(p, sz);
p = rhs.p;
sz = rhs.sz;
rhs.p = 0;
rhs.sz = 0;
}
return *this;
}
13.50
略
13.51
unique_ptr不能拷贝,但有一个例外.将要被销毁的unique_ptr是可以拷贝或销毁的.因此.在418页的clone函数中返回局部unique_ptr对象ret是可以的。因为ret马上就要被销毁了.而此时的"拷贝".其实是触发移动构造函数进行了移动.
13.52
对hp=hp2因为hp2是一个变量.是一个左值.因此它传递给赋值运算符参数rhs的过程是拷贝构造过程.rhs获得hp2的一个剧本.rhs.ps与hp2.ps指向不同的string.但两个string包含相同的内容.在赋值运算符中.交换hp和rhs.rhs指向hp原来的string.在赋值结束后被销毁.最终结果.hp和hp2指向两个独立的string.但内容相同.
对hp=std::move(hp2).hp2传递给rhs的过程是移动构造过程.rhs.ps指向hp2.ps原来的string.hp2的ps被设置为空指针.然后赋值运算符交换hp和rhs.rhs指向原来的string.在赋值结束后被销毁.最终结果hp指向hp2原来的string.而hp2则变成空.
13.53
例如:
inline HasPtr& HasPtr::operator=(HasPtr &&rhs) noexcept{
cout<<"Move Assignment"<<endl;
if(this!=&rhs){
delete ps;
ps=rhs.ps;
rhs.ps=nullptr;
rhs.i=0;
}
return *this;
}
13.54
会产生编译错误
因为对于hp=std::move(hp2)这样的赋值语句来说.两个运算符匹配的一样好.从而产生了二义性
13.55
void push_back(string &&t){data->push_back(std::move(t));}
13.56
局部变量ret拷贝了被调用对象的一个副本.然后,对ret调用sorted.由于并非是函数返回语句或函数结束.因此编译器认为它是左值.仍然调用左值引用版本.产生递归循环.
利用右值引用版本来完成排序的期望不能实现.
13.57
是正确的利用右值引用版本来完成排序.原因在于,编译器认为
Foo(*this)
是一个无主的右值.对它调用sorted会匹配右值引用版本.