C++primer第十三章(第五版)答案

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(); 
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值