关闭

c++ primer(第五版)学习笔记及习题答案代码版(第十三章)拷贝控制

标签: c++ primer习题答案c++primerc++语法拷贝控制c++11
1257人阅读 评论(0) 收藏 举报
分类:
笔记较为零散,都是自己不熟悉的知识点。

习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行13.30题为#define NUM1330;

chapter 13

1. 拷贝构造函数不应该是explicit的。每个成员的类型决定了它如何拷贝:对类类型的成员,会使用其拷贝构造函数来

拷贝;内置类型的成员则直接拷贝。虽然我们不能直接拷贝一个数组,但合成拷贝狗杂函数会逐元素地拷贝一个数组类型的成员。

当初化标准库容器或是调用其insert或push成员时,容器会对其元素进行拷贝初始化,相对的,用emplace成员创建的元素都进行

直接初始化。

为了调用拷贝构造函数,我们必须拷贝实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此循环,所以构造函数被用来初始化

非引用类类型参数,解释了为什么拷贝狗仔函数自己的参数一定是引用类型。

2.  为与内置类型的赋值保持一致,赋值运算符通常返回一个值指向其左侧运算对象的引用。另外,标准库通常要求保存在容器中的类型要具有

赋值运算符,且其返回值是左侧运算对象的引用。

由于析构函数不接受参数,因此它不能够被重载。对一个给定类,只会有唯一一个析构函数。隐式销毁一个内置指针类型的成员不会delete它所指向的对象。

与普通指针不同,智能指针是类类型,所以具有析构函数。

当一个对象的引用或指针离开作用域时,析构函数不会执行。当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。

3. 如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。

第二个基本原则:如果一个类需要一个拷贝构造函数,几乎可以肯定的它也需要一个拷贝赋值运算符。反之亦然。但是,无论是需要拷贝构造函数还是需要拷贝

运算符都不意味着也需要析构函数。

我们只能对具有合成版本的成员函数使用=default(即,默认构造函数或拷贝控制成员)

4. 阻止拷贝。比如,iostream类阻止了拷贝,以避免多个对象写入或读取相同的IO缓冲。即使不定义拷贝控制函数,编译器也会生成合成的版本。

新版本下,我们通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝。

合成的拷贝控制成员是可以删除的。本质上,这些规则P450的含义是:如果一个类有数据成员不能默认构造、拷贝、复制或销毁、则对应的成员函数

将定义为删除的。

5. 通常管理类外资源的类必须定义拷贝控制成员。为了定义这些成员,首先必须确定此类型对象的拷贝语义。一般来说有两种选择:可以定义拷贝操作,使

类看起来像一个值或像一个指针。值和指针的区别是,值由自己的状态,拷贝一个像值的对象,副本和原对象完全独立,而指针则共享状态。

当用标准库时,容器和string类的行为像一个值。而不出意外的,shared_ptr类提供类似指针的行为,像StrBlob类一样。

IO类型和unique_ptr不允许拷贝和赋值,因此它们的行为既不像值也不像指针。

6. 定义行为像指针的类。引用计数需要确定在哪里存放引用计数。计数器不能作为HasPtr对象的成员。一种方法是将计数器保存在动态内存中。当创建一个对象

时,我们分配一个新的计数器。当拷贝或赋值对象时,拷贝指向计数器的指针。这种方式,副本和原对象都会指向相同的计数器。

7.  与拷贝控制成员不同,swap并不是必要的,但对于分配了资源的类swap是个优化手段。swap函数应该调用swap而不是std::swap

标准库swap对HasPtr管理的string进行了不必要的拷贝。如果一个类的成员有自己类型特定的swap函数,调用std:swap就是错误的。

如果存在类型特定的swap版本,其匹配程度会优于std中定义的版本。

拷贝赋值运算符通常执行拷贝构造函数和析构函数中也要做的工作。这时,公共的工作应该放在private工具函数中完成。

8.  StrVec的设计:vector的每个添加元素的成员函数会检查是否有空间容纳更多的元素。如果有,成员函数会在下一个可用位置构造一个对象。

如果没有可用空间,vector就会重新分配空间:它获得新的空间,将已有元素移动到新空间,释放旧空间,并添加新元素。

free成员,一旦元素被销毁,就调用deallocate来释放StrVec对象分配的内存空间,我们传递给deallocate的指针必须是之前某次allocate调用所返回的

指针,因此在调用deallocate之前我们首先检查elements是否为空。

9. 在重新分配内存的过程中移动而不是拷贝元素。当拷贝一个string时,新string和原string是相互独立的。

由于string的行为类似值,每个string对构成它的所有字符都会保存自己的一份副本。拷贝一个string必须为这些字符 分配内存空间。一旦将元素拷贝到新空间,

就会立即销毁原string。所以,拷贝这些string中的数据是多余的。

move标准库函数定义在utility中。两个要点,当reallocate在新内存中构造string时,它必须调用move来表示希望使用string的移动构造函数。

如果漏掉move操作,将使用string的拷贝构造函数。另外,不为move提供using声明。

10.  新标准的移动对象的能力。在重新分配内存的过程中,从旧内存将元素拷贝到新内存是不必要的,更好的方式是移动元素。

这一特定源于IO类或unique_ptr这些类。它们包括被共享的资源,因此,这些类的对象不能拷贝只能移动。

新标准的引用类型---右值引用为了支持移动操作。也就是必须绑定到右值的引用。一个重要的特性是只能绑定到一个将要销毁的对象。

由此可以自由的将一个右值引用的资源“移动”到另一个对象中。

一般,左值表达对象的身份,右值表示的是对象的值。一般引用(左值引用)不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。我们可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用绑定到左值上。

左值有持久的状态,右值要么是字面常量。要么是在表达式求值过程中创建的临时变量。

int &&rr2 = rr1;  //错误:表达式rr1是左值。

变量是左值,不能将一个右值引用直接绑定到一个变量上。

我们可以销毁一个移后源对象,也可以赋予它新值,但不能使用一个移后源对象的值。对于move操作,直接调用std::move.避免名字冲突。

11.  移动构造函数类似于拷贝构造函数,第一参数是该类类型的一个引用,但这个引用参数在移动构造函数中是一个右值引用。

不抛出异常的移动构造函数和移动赋值运算符必须标记为noexpect。

在移动操作之后,移后源对象必须保持有效的、可析构的状态,但是用户不能对其值进行任何假设。

一个移动迭代器适配器通过改变给定迭代器的解引用运算符的行为来适配次迭代器。一般,一个迭代器 的解引用运算符返回一个指向

元素的左值,但是移动迭代器的解引用运算符生成一个右值引用。

Alice Emma has long flowing red hair. 
Her Daddy says when the wind blows 
through her hair, it looks almost alive, 
like a fiery bird in flight. 
A beautiful fiery bird, he tells her, 
magical but untamed. 
"Daddy, shush, there is no such thing," 
she tells him, at the same time wanting 
him to tell her more.
Shyly, she asks, "I mean, Daddy, is there?"
//Chapter13.h
#ifndef CHAPTER13_H
#define CHAPTER13_H

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <initializer_list>
#include <map>
#include <set>
#include <memory>
#include <algorithm>
#include <exception>
using namespace::std;

//13.5
class HasPtr{
public:
  HasPtr(const string &s = string()) : ps(new string(s)), i(10){}
  HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) { }  //new string()
public:
  string *ps;
  int i;
};
//13.8
class HasPtr_8{
public:
  HasPtr_8(const string &s = string()):
    ps(new string(s)), i(10){}
  HasPtr_8(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) { }  
  HasPtr_8& operator=(const HasPtr &rhs){
    string * new_ps = new string(*rhs.ps);
    delete ps;
    ps = new_ps;
    i = rhs.i;
    return *this;
  }
  ~HasPtr_8(){ delete ps; }     //13.11
public:
  string *ps;
  int i;
};
//13.13
struct X {
    X() {cout << "X()" << endl;}
    X(const X&) {cout << "X(const X&)" << endl;}
    X& operator=(const X&){
    	cout << "X& operator=(const X&)"<<endl;
    	return *this;
    }
    ~X(){ cout << "~X(){ }" <<endl;  }
};

//13.14
class numbered{
public:
	numbered(){
		int unique = 10;
		mysn = unique++;
	}
	int mysn;
};
void func_14 (numbered s) { 
	cout << s.mysn<< endl; 
}

//13.15
class numbered_15{
public:
	numbered_15(){
		int unique = 10;
		mysn = unique++;
	}
	numbered_15(numbered_15& rhs){ mysn = rhs.mysn + 1; }
	int mysn;
};
void func_15 (numbered_15 s) { 
	cout << s.mysn<< endl; 
}
//13.16
class numbered_16{
public:
	numbered_16(){
		int unique = 10;
		mysn = unique++;
	}
	numbered_16(numbered_16& rhs){mysn = rhs.mysn + 1;}
	int mysn;
};
void func_16 (const numbered_16& s) { 
	cout << s.mysn<< endl; 
}

//13.18
class Employee{
public:
	Employee(){ ID = unique++;	}
	Employee(const string& na) : name(na){ ID = unique++; }
	Employee(const Employee&) = delete;
    Employee& operator=(const Employee&) = delete;
	const int id() const{ return ID;}
private:
	string name;
	int ID;
	static int unique;
};
int Employee::unique = 0;

//13.22
class HasPtr_22{
public:
  HasPtr_22(const string &s = string()) : ps(new string(s)), i(0){}
  HasPtr_22(const HasPtr_22& hp) : ps(new string(*hp.ps)), i(hp.i) { } 
  HasPtr_22& operator=(const HasPtr_22& rhs){
  	auto new_ptr = new string(*rhs.ps);
  	delete ps;
  	ps = new_ptr;
  	i = rhs.i;
  	return *this;
  }
  ~HasPtr_22(){delete ps;}
public:
  string *ps;
  int i;
};
//13.24
class HasPtr_24{
public:
  HasPtr_24(const string &s = string()) : ps(new string(s)), i(0){}
  HasPtr_24& operator=(const HasPtr_24& rhs){
  	auto new_ptr = new string(*rhs.ps);
  	delete ps;
  	ps = new_ptr;
  	i = rhs.i;
  	return *this;
  }
  string& get_24(){ return *this->ps;}
  ~HasPtr_24(){delete ps;}
public:
  string *ps;
  int i;
};

//13.26
class ConstStrBlobPtr;
class StrBlob {
public:
    using size_type = vector<string>::size_type;
    friend class ConstStrBlobPtr;

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

    StrBlob() : data(make_shared<vector<string>>()) {}
    StrBlob(initializer_list<string> il)
        : data(make_shared<vector<string>>(il))
    {
    }
    // copy constructor
    StrBlob(const StrBlob& sb) : data(make_shared<vector<string>>(*sb.data)) { }
    // copyassignment operators
    StrBlob& operator=(const StrBlob& sb){
    data = make_shared<vector<string>>(*sb.data);
    return *this;
}
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

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

	void push_back(string &&s){                    //13.55
		data-> push_back(std::move(s));
	}
	void print(){
		for(auto &i : *data) cout << i << " ";
	}
    void pop_back()  {
        check(0, "pop_back on empty StrBlob");
        data->pop_back();
    }
    string& front() {
        check(0, "front on empty StrBlob");
        return data->front();
    }
   string& back() {
        check(0, "back on empty StrBlob");
        return data->back();
    }
    const string& front() const {
        check(0, "front on empty StrBlob");
        return data->front();
    }
    const string& back() const {
        check(0, "back on empty StrBlob");
        return data->back();
    }
private:
    void check(size_type i, const string& msg) const {
        if (i >= data->size()) throw out_of_range(msg);
    }
    shared_ptr<vector<string>> data;
};

class ConstStrBlobPtr {
public:
    ConstStrBlobPtr() : curr(0) {}
    ConstStrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {} // should add const
    bool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; }
    const string& deref() const    { // return value should add const
        auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    ConstStrBlobPtr& incr()    {
        check(curr, "increment past end of StrBlobPtr");
        ++curr;
        return *this;
    }
private:
    shared_ptr<vector<string>> check(size_t i, const string& msg) const    {
        auto ret = wptr.lock();
        if (!ret) throw runtime_error("unbound StrBlobPtr");
        if (i >= ret->size()) throw out_of_range(msg);
        return ret;
    }
    weak_ptr<vector<string>> wptr;
    size_t curr;
};
ConstStrBlobPtr StrBlob::begin() const {
    return ConstStrBlobPtr(*this);
}
ConstStrBlobPtr StrBlob::end() const {
    return ConstStrBlobPtr(*this, data->size());
}
//13.27
class HasPtr_27{
public:
    HasPtr_27(const string& s = string()): ps(new string(s)), i(0), use(new size_t(1)){  }
    HasPtr_27(const HasPtr_27& hp) : ps(hp.ps), i(hp.i), use(hp.use){ ++*use; }
    HasPtr_27& operator =(const HasPtr_27& rhs){
        ++*rhs.use;
        if(--*use == 0){
            delete ps;
            delete use;
        }
        ps = rhs.ps;
		i = rhs.i;
		use = rhs.use;
        return *this;
    }
    ~HasPtr_27(){
        if(--*use == 0){
            delete ps;
            delete use;
        }
    }
private:
    string* ps;
    int i;
    size_t* use;
};

//13.28
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=(const TreeNode& rhs);
    ~TreeNode()    {
        if (--*count == 0) {
            if (left) {
                delete left;
                left = nullptr;
            }
            if (right) {
                delete right;
                right = nullptr;
            }
            delete count;
            count = nullptr;
        }
    }
private:
    string value;
    int* count;
    TreeNode* left;
    TreeNode* right;
};
TreeNode& TreeNode::operator=(const TreeNode& rhs){
    ++*rhs.count;
    if (--*count == 0) {
        if (left) {
            delete left;
            left = nullptr;
        }
        if (right) {
            delete right;
            right = nullptr;
        }
        delete count;
        count = nullptr;
    }
    value = rhs.value;
    left = rhs.left;
    right = rhs.right;
    count = rhs.count;
    return *this;
}

class BinStrTree {
public:
    BinStrTree() : root(new TreeNode()) {}
    BinStrTree(const BinStrTree& bst) : root(new TreeNode(*bst.root)) {}
    BinStrTree& operator=(const BinStrTree& bst);
    ~BinStrTree() { delete root; }

private:
    TreeNode* root;
};

BinStrTree& BinStrTree::operator=(const BinStrTree& bst){
    TreeNode* new_root = new TreeNode(*bst.root);
    delete root;
    root = new_root;
    return *this;
}    

//13.30
class HasPtr_30 {
public:
    friend void swap(HasPtr_30&, HasPtr_30&);
    HasPtr_30(const string& s = string()) : ps(new string(s)), i(0){ }
    HasPtr_30(const HasPtr_30& hp) : ps(new string(*hp.ps)), i(hp.i) {}
    HasPtr_30& operator=(const HasPtr_30& hp)  {
        auto new_p = new string(*hp.ps);
        delete ps;
        ps = new_p;
        i = hp.i;
        return *this;
    }
    ~HasPtr_30() { delete ps; }

    void show() { cout << *ps << endl; }
private:
    string* ps;
    int i;
};

void swap(HasPtr_30& lhs, HasPtr_30& rhs){
    using std::swap;
    swap(lhs.ps, rhs.ps);
    swap(lhs.i, rhs.i);
    cout << "call swap(HasPtr_30& lhs, HasPtr_30& rhs)" << endl;
}

//13.31
class HasPtr_31 {
public:
    friend void swap(HasPtr_31&, HasPtr_31&);
    friend bool operator<(const HasPtr_31& lhs, const HasPtr_31& rhs);
    HasPtr_31(const string& s = string()) : ps(new string(s)), i(0) { }
    HasPtr_31(const HasPtr_31& hp) : ps(new string(*hp.ps)), i(hp.i) {}
    HasPtr_31& operator=(HasPtr_31 tmp)    {
        this->swap(tmp);
        return *this;
    }
    ~HasPtr_31() { delete ps; }
    void swap(HasPtr_31& rhs)    {
        using std::swap;
        swap(ps, rhs.ps);
        swap(i, rhs.i);
        cout << "call swap(HasPtr_31 &rhs)" << endl;
    }
    void show() const { cout << *ps << endl; }
private:
    string* ps;
    int i;
};
void swap(HasPtr_31& lhs, HasPtr_31& rhs){
    lhs.swap(rhs);
}
bool operator<(const HasPtr_31& lhs, const HasPtr_31& rhs){
    return *lhs.ps < *rhs.ps;
}

//13.34 13.36 13.37
class Folder;
class Message {
    friend void swap(Message&, Message&);
    friend void swap(Folder&, Folder&);
    friend class Folder;
public:
    explicit Message(const string& str = "") : contents(str) {}
    Message(const Message&);
    Message& operator=(const Message&);
    Message(Message&&);                         //13.49
    Message& operator=(Message&&);              //13.49
    ~Message();
    void save(Folder&);
    void remove(Folder&);

    void print_debug();
private:
    string contents;
    set<Folder*> folders;

    void add_to_Folders(const Message&);
    void remove_from_Folders();
    void move_Folders(Message*);            //13.49
    void addFldr(Folder* f) { folders.insert(f); }
    void remFldr(Folder* f) { folders.erase(f); }
};
void swap(Message&, Message&);

class Folder {
    friend void swap(Message&, Message&);
    friend void swap(Folder&, Folder&);
    friend class Message;
public:
    Folder() = default;
    Folder(const Folder&);
    Folder& operator=(const Folder&);
    Folder(Folder&&);                       //13.49
    Folder& operator=(Folder&&);            //13.49
    ~Folder();
    void print_debug();
private:
    set<Message*> msgs;
    void add_to_Messages(const Folder&);
    void remove_from_Messages();
    void move_Messages(Folder*);            //13.49
    void addMsg(Message* m) { msgs.insert(m); }
    void remMsg(Message* m) { msgs.erase(m); }
};

void swap(Folder&, Folder&);

void swap(Message& lhs, Message& rhs){
    using std::swap;
    lhs.remove_from_Folders();
    rhs.remove_from_Folders();
    swap(lhs.folders, rhs.folders);
    swap(lhs.contents, rhs.contents);
    lhs.add_to_Folders(lhs);
    rhs.add_to_Folders(rhs);
}

// Message Implementation
Message::Message(const Message& m) : contents(m.contents), folders(m.folders){
    add_to_Folders(m);
}

Message& Message::operator=(const Message& rhs){
    remove_from_Folders();
    contents = rhs.contents;
    folders = rhs.folders;
    add_to_Folders(rhs);
    return *this;
}

Message::Message(Message&& m) : contents(move(m.contents)){  //13.49
    move_Folders(&m);
}

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

Message::~Message(){
    remove_from_Folders();
}

void Message::save(Folder& f){
    addFldr(&f);
    f.addMsg(this);
}

void Message::remove(Folder& f){
    remFldr(&f);
    f.remMsg(this);
}

void Message::print_debug(){
    cout << contents << endl;
}

void Message::add_to_Folders(const Message& m){
    for (auto f : m.folders) f->addMsg(this);
}

void Message::remove_from_Folders(){
    for (auto f : folders) f->remMsg(this);
}

void Message::move_Folders(Message* m){          //13.49
    folders = move(m->folders);
    for (auto f : folders) {
        f->remMsg(m);
        f->addMsg(this);
    }
    m->folders.clear();
}
// Folder Implementation
void swap(Folder& lhs, Folder& rhs){
    using std::swap;
    lhs.remove_from_Messages();
    rhs.remove_from_Messages();

    swap(lhs.msgs, rhs.msgs);

    lhs.add_to_Messages(lhs);
    rhs.add_to_Messages(rhs);
}

Folder::Folder(const Folder& f) : msgs(f.msgs){
    add_to_Messages(f);
}

Folder& Folder::operator=(const Folder& rhs){
    remove_from_Messages();
    msgs = rhs.msgs;
    add_to_Messages(rhs);
    return *this;
}

Folder::Folder(Folder&& f){         //13.49
    move_Messages(&f);
}

Folder& Folder::operator=(Folder&& f){   //13.49
    if (this != &f) {
        remove_from_Messages();
        move_Messages(&f);
    }
    return *this;
}

Folder::~Folder(){
    remove_from_Messages();
}

void Folder::print_debug(){
    for (auto m : msgs) cout << m->contents << " ";
    cout << endl;
}

void Folder::add_to_Messages(const Folder& f){
    for (auto m : f.msgs) m->addFldr(this);
}

void Folder::remove_from_Messages(){
    for (auto m : msgs) m->remFldr(this);
}

void Folder::move_Messages(Folder* f){    //13.49
    msgs = move(f->msgs);
    for (auto m : msgs) {
        m->remFldr(f);
        m->addFldr(this);
    }
    f->msgs.clear();
}

//13.39
class StrVec {
public:
    StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
    StrVec(const StrVec&);
    StrVec(initializer_list<string>);   //13.40
    StrVec& operator=(const StrVec&);
    ~StrVec();
	StrVec(const string*, const string*);

    void push_back(const string&);
    size_t size() const { return first_free - elements; }
    size_t capacity() const { return cap - elements; }
    string* begin() const { return elements; }
    string* end() const { return first_free; }

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

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

private:
    pair<string*, string*> alloc_n_copy(const string*,
                                                       const string*);
    void free();
    void chk_n_alloc()
    {
        if (size() == capacity()) reallocate();
    }
    void reallocate();
    void alloc_n_move(size_t new_cap);
	void range_initialize(const string*, const string*);  //13.40
private:
    string* elements;
    string* first_free;
    string* cap;
    allocator<string> alloc;
};

StrVec::StrVec(initializer_list<string> li){
	range_initialize(li.begin(), li.end());
}
void StrVec::push_back(const string& s){
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

pair<string*, string*> StrVec::alloc_n_copy(const string* b,
                                                           const string* e){
    auto data = alloc.allocate(e - b);
    return {data, uninitialized_copy(b, e, data)};
}

void StrVec::free(){
    if (elements) {
        for (auto p = first_free; p != elements;) alloc.destroy(--p);
        alloc.deallocate(elements, cap - elements);
    }
}

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

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

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

StrVec::StrVec(const string* b, const string* e){
	pair<string*, string*> newdata = alloc_n_copy(b, e);
	elements = newdata.first;
	first_free = cap = newdata.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::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++, move(*elem++));
    free();
    elements = newdata;
    first_free = dest;
    cap = elements + new_cap;
}

void StrVec::reallocate(){
    auto newcapacity = size() ? 2 * size() : 1;
    alloc_n_move(newcapacity);
}

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

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

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

//13.42
class QueryResult;
class TextQuery {
public:
    TextQuery(ifstream&);
    QueryResult query(const string&) const;

private:
    shared_ptr<StrVec> input;
    map<string, shared_ptr<set<size_t>>> result;
};

class QueryResult {
public:
    friend ostream& print(ostream&, const QueryResult&);

public:
    QueryResult(const string& s, shared_ptr<set<size_t>> set,
                shared_ptr<StrVec> v)
        : word(s), nos(set), input(v)  {  }

private:
    string word;
    shared_ptr<set<size_t>> nos;
    shared_ptr<StrVec> input;
};

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

TextQuery::TextQuery(ifstream& ifs) : input(new StrVec){
    size_t lineNo = 0;
    for (string line; getline(ifs, line); ++lineNo) {
        input->push_back(line);
        istringstream line_stream(line);
        for (string text, word; line_stream >> text; word.clear()) {
            // avoid read a word followed by punctuation(such as: word, )
            remove_copy_if(text.begin(), text.end(),
                                back_inserter(word), ::ispunct);
            // use reference avoid count of shared_ptr add.
            auto& nos = result[word];
            if (!nos) nos.reset(new set<size_t>);
            nos->insert(lineNo);
        }
    }
}

QueryResult TextQuery::query(const string& str) const{
    // use static just allocate once.
    static shared_ptr<set<size_t>> nodate(new set<size_t>);
    auto found = result.find(str);
    if (found == result.end())
        return QueryResult(str, nodate, input);
    else
        return QueryResult(str, found->second, input);
}

ostream& print(ostream& out, const QueryResult& qr){
    out << qr.word << " occurs " << qr.nos->size()
        << (qr.nos->size() > 1 ? " times" : " time") << endl;
    for (auto i : *qr.nos)
        out << "\t(line " << i + 1 << ") " << qr.input->at(i)<< endl;
    return out;
}

//13.44 13.47
class String {
public:
    String() : String("") {}
    String(const char*);
    String(const String&);
    String& operator=(const String&);
    ~String();

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

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

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

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

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

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

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

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

//13.53
class HasPtr_53 {
public:
    friend void swap(HasPtr_53&, HasPtr_53&);
    HasPtr_53(const string& s = string());
    HasPtr_53(const HasPtr_53& hp);
    HasPtr_53(HasPtr_53&& p) noexcept;
    HasPtr_53& operator=(HasPtr_53 rhs);
    // HasPtr& operator=(const HasPtr &rhs);
    // HasPtr& operator=(HasPtr &&rhs) noexcept;
    ~HasPtr_53();

private:
    string* ps;
    int i;
};

inline void swap(HasPtr_53& lhs, HasPtr_53& rhs){
    using std::swap;
    swap(lhs.ps, rhs.ps);
    swap(lhs.i, rhs.i);
    cout << "call swap" << endl;
}

HasPtr_53::HasPtr_53(const string& s) : ps(new string(s)), i(0){
    cout << "call constructor" << endl;
}

HasPtr_53::HasPtr_53(const HasPtr_53& hp) : ps(new string(*hp.ps)), i(hp.i){
    cout << "call copy constructor" << endl;
}

HasPtr_53::HasPtr_53(HasPtr_53&& p) noexcept : ps(p.ps), i(p.i){
    p.ps = 0;
    cout << "call move constructor" << endl;
}

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

// HasPtr_53& HasPtr_53::operator=(const HasPtr_53 &rhs){
//    auto newp = new string(*rhs.ps);
//    delete ps;
//    ps = newp;
//    i = rhs.i;
//    cout << "call copy assignment" << endl;
//    return *this;
//}

// HasPtr_53& HasPtr_53::operator=(HasPtr_53 &&rhs) noexcept{
//    if (this != &rhs)
//    {
//        delete ps;
//        ps = rhs.ps;
//        i = rhs.i;
//        rhs.ps = nullptr;
//        cout << "call move assignment" << endl;
//    }
//    return *this;
//}

HasPtr_53::~HasPtr_53(){
    cout << "call destructor" << endl;
    delete ps;
}

//13.58
class Foo {
public:
    Foo sorted()&&;
    Foo sorted() const&;

private:
    vector<int> data;
};

Foo Foo::sorted() &&{
    sort(data.begin(), data.end());
    cout << "&&" << endl; // debug
    return *this;
}

Foo Foo::sorted() const &{
    //    Foo ret(*this);
    //    sort(ret.data.begin(), ret.data.end());
    //    return ret;

    cout << "const &" << endl; // debug

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

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

#endif 

//main.cc
#include <fstream>
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include "Chapter13.h"
using namespace std;
#define NUM1358

/*13.13*/
void func(const X& rx, X x){
	vector<X> vec;
	vec.reserve(2);
	vec.push_back(rx);
	vec.push_back(x);
}
/*13.31*/
bool compare(HasPtr_31 a, HasPtr_31 b){
	return a < b;
}
/*13.39*/
StrVec getVec(istream &is){
	string word;
	StrVec vec;
	while(is >> word){
		vec.push_back(word);
	}
	return vec;
}
void print_39(StrVec &vec){
	for(auto &i : vec)
		cout<< i << " ";
	cout << endl;
}

/*13.48*/
void foo(String x){
    cout << x.c_str() << endl;
}

void bar(const String& x){
    cout << x.c_str() << endl;
}

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


int main(){
/*13.1*/
#ifdef NUM131
	cout << "拷贝构造函数是一种特殊的构造函数,它的第一个参数是自身类型的引用。"
			"以下情况调用拷贝构造函数:"
			"当用=定义变量"
			"一个对象以值传递的方式传入函数体 "
			"一个对象以值传递的方式从函数返回 "
			"一个对象需要通过另外一个对象进行初始化。"
			"用花括号列表初始化一个数组中的元素或一个聚合类中的成员。"
			"某些类类型还会对它们所分配的对象使用拷贝初始化。"<<endl;
#endif
/*13.2*/
#ifdef NUM132
	cout <<"这一方式永远不会调用拷贝构造函数,因为非引用类型的参数要进行拷贝初始化。"
			"Sales_data rhs是一个形参,因此我们需要调用拷贝构造函数来拷贝构造函数,但是为了拷"
			"贝实参,又需要调用拷贝构造函数,以此往复。"<<endl;
#endif
/*13.3*/
#ifdef NUM133
	cout <<"当拷贝一个StrBlob时,shared_ptr成员的引用计数加1;"
			"当拷贝StrBlobPtr时,week_ptr成员的计数不变。" <<endl;
#endif
/*13.4*/
#ifdef NUM134
/*	Point global;
	Pointfoo_bar(Point arg){              		  // 1
	Point local = arg, *heap = newPoint(global); // 2, 3
    *heap = local;
	Point pa[ 4 ] = { local, *heap };           // 4, 5
	return *heap;                               // 6
}
*/
#endif
/*13.5*/
#ifdef NUM135
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.6*/
#ifdef NUM136
	cout<<"拷贝赋值运算符是一个名为operator=的函数。当出现赋值操作时使用。"
		"合成拷贝赋值运算符将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员,"
		"这一工作通过成员类型的拷贝赋值运算符来完成。"
		"当一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。"<<endl;
#endif
/*13.7*/
#ifdef NUM137
	cout<<"右侧运算对象的每个非static成员赋予左侧运算对象的对应成员,"
			"StrBlob的引用计数加1,StrBlobPtr的引用计数不变。"<<endl;
#endif
/*13.8*/
#ifdef NUM138
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.9*/
#ifdef NUM139
	cout<<"析构函数时类的成员函数,名字由(~)加类名组成,不接受参数,也没有返回值。"
		"当一个类未定义自己的析构函数时,编译器会定义一个合成析构函数。合成析构函数用来阻"
		"止该类型的对象被销毁。如果不是这样,合成析构函数体内就为空。"<<endl;
#endif
/*13.10*/
#ifdef NUM1310
	cout<<"StrBlob对象销毁时,use_count计数器递减,如果没有对象的共享类型,它的内存会被释放。"
		"当StrBlobPtr被销毁时,动态分配的对象并不会被释放。"<<endl;
#endif
/*13.11*/
#ifdef NUM1311
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.12*/
#ifdef NUM1312
	cout<< "3次,accum, item1 and item2.发生了拷贝,会自动调用析构函数."<<endl;
#endif
/*13.13*/
#ifdef NUM1313
	X *ex1 = new X;
    func(*ex1, *ex1);
    delete ex1;
#endif
/*13.14*/
#ifdef NUM1314
	numbered a, b = a, c = b;
	func_14(a); func_14(b); func_14(c);
#endif
/*13.15*/
#ifdef NUM1315
	numbered_15 a, b = a, c = b;
	func_15(a); func_15(b); func_15(c);
	cout <<"因为没有用合成拷贝控制成员,而是自己定义得。输出为不同的数。"<<endl;
#endif
/*13.16*/
#ifdef NUM1316
	numbered_16 a, b = a, c = b;
	func_16(a); func_16(b); func_16(c);
	cout <<"因为f函数没有任何拷贝操作,所以输出的值和传入f函数时候相同。"<<endl;
#endif
/*13.18*/
#ifdef NUM1318
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.19*/
#ifdef NUM1319
	cout << "理论上可以,但是不需要,人员类不可以拷贝。"<<endl;
#endif
/*13.20*/
#ifdef NUM1320
	cout << "TextQuery/QueryResult中的成员、智能指针将发生拷贝操作。"<<endl;
#endif
/*13.21*/
#ifdef NUM1321
	cout << "没有必要,因为两个类型之间通过智能指针实现动态内存与数据共享,自动调用合成析构函数释放内存。"<<endl;
//	TextQuery(const TextQuery&) = delete;
//	TextQuery&operator=(const TextQuery) = delete;

//	QueryResult(const QueryResult&) = delete;
//	QueryResult&operator=(const QueryResult) = delete;
#endif
/*13.22*/
#ifdef NUM1322
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.24*/
#ifdef NUM1324
	cout<<"如果析构函数没有定义,将发生内存泄漏;如果拷贝函数没有定义,只有指针被拷贝了,ps指向的string并没有拷贝。"<<endl;
	HasPtr_24 aa("hello"), bb;
	bb = aa;
	cout << bb.get_24() <<endl;
#endif
/*13.25*/
#ifdef NUM1325
	cout<< "拷贝构造和拷贝赋值运算符动态分配内存,而不是与=右侧的对象共享对象。"
		"StrBlob的智能指针由合成析构函数管理,如果StrBlob对象离开作用域,智能指针的析构函数将被自动调用释放内存。"<<endl;
#endif
/*13.26*/
#ifdef NUM1326
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.27*/
#ifdef NUM1327
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.28*/
#ifdef NUM1328
	cout <<"见Chapter13.h"<<endl;
#endif
/*13.29*/
#ifdef NUM1329
  	cout << "函数体中,swap(lhs.ps, rhs.ps);匹配swap(string*, string*);"
  	"并且swap(lhs.i, rhs.i);匹配swap(int, int);而不会调用swap(HasPtr&, HasPtr&);因此不会导致递归调用。"<<endl;
#endif
/*13.30*/
#ifdef NUM1330
	HasPtr_30 test1("hello"), test2("world");
	swap(test1, test2);
	test1.show(); 
	test2.show();
#endif
/*13.31*/
#ifdef NUM1331
	HasPtr_31 a( "dd"), b("bb"), c("aa"), d("zz"), e("kk");
	vector<HasPtr_31> vec{a, b, c, d, e};
//	sort(vec.begin(), vec.end());
	sort(vec.begin(), vec.end(), compare);
	for(auto &i : vec)
		i.show();
#endif
/*13.32*/
#ifdef NUM1332
  	cout<<"新标准提高性能的标准是避免内存分配。对于类指针版本,反正没有动态内存分配。因此,并不会受益。"<<endl;
#endif
/*13.33*/
#ifdef NUM1333
  	cout <<"以为这些操作也要更新指定的Folder对象,Folder类通过它的addMsg和remMsg成员控制add或remove指向给定Message. "<<endl;
#endif
/*13.34*/
#ifdef NUM1334
	cout << "见Chapter13.h"<<endl;
#endif
/*13.35*/
#ifdef NUM1335
	cout << "一些现有的Folders在赋值之后不再与Message同步。"<<endl;
#endif
/*13.36*/
#ifdef NUM1336
	cout << "见Chapter13.h"<<endl;
#endif
/*13.37*/
#ifdef NUM1337
 	Message firstMail("hello");
    Message signInMail("welcome to c++rimer");
    Folder mailBox;

    firstMail.print_debug(); // print: "hello"
    firstMail.save(mailBox); // send to mailBox
    mailBox.print_debug();   // print: "hello"

    signInMail.print_debug(); // print "welcome to c++primer"
    signInMail.save(mailBox); // send to mailBox
    mailBox.print_debug();    // print "hello welcome to c++primer"
	
	swap(firstMail, signInMail);
	mailBox.print_debug();    //print "welcome to c++primer hello"

    firstMail = firstMail;   // test for assignment to self.
    firstMail.print_debug(); // print "hello"
    mailBox.print_debug();   // print "hello welcome to c++primer"
#endif
/*13.38*/
#ifdef NUM1338
	cout <<"在Message类中,并没有内存的动态分配。因此利用拷贝和swap方式反而变得复杂。"<<endl;
#endif
/*13.39*/
#ifdef NUM1339
	string temp[] = {"one", "two", "three"};
	StrVec sv(temp, temp + sizeof(temp)/sizeof(*temp));
//	if(!sv[0].empty())
//		sv[0] = "None";
	print_39(sv);
	ifstream in("./storyDataFile");
	StrVec vec = getVec(in);
	print_39(vec);
	in.close();
	
	cout << "copy test: "<< vec.size() <<endl;
	StrVec vec2 = vec;
	print_39(vec2);
	
	cout <<" assignment test: "<<endl;
	StrVec vec3;
	vec3 = vec2;
	print_39(vec3);

	StrVec v1, v2;
	v1 = v2;
	in.open("./storyDataFile");
	v2 = getVec(in);
	print_39(v2);
	in.close();
	
	StrVec vec4;
	string ss{"hello world"};
	vec4.push_back(ss);
	vec4.push_back("hello ");
	print_39(vec4);
#endif
/*13.40*/
#ifdef NUM1340
	StrVec vec{"one ", "two", "three"};
	print_39(vec);
#endif
/*13.41*/
#ifdef NUM1341
/*	
|a|b|c|d|f|..............|
^          ^             ^
elements   first_free    cap

// if use alloc.construct(first_free++, "g");
|a|b|c|d|f|g|.............|
^            ^            ^
elements     first_free   cap

// if use alloc.construct(++first_free, "g");
|a|b|c|d|f|.|g|............|
^          ^ ^             ^
elements   | first_free    cap
           |
    "unconstructed"
*/
#endif
/*13.42*/
#ifdef NUM1342
    ifstream infile("./storyDataFile");
    TextQuery tq(infile);
    while (true) {
        cout << "enter word to find, q for quit: ";
        string s;
        if (!(cin >> s) || s == "q") break;
        print(cout, tq.query(s)) << endl;
    }
#endif
/*13.43*/
#ifdef NUM1343
	for_each(elements, first_free, [this](string &rhs){ alloc.destroy(&rhs); });
	cout << "很显然新版本更直接简单,不需要考虑顺序和递减的因素. "<<endl;
#endif
/*13.44*/
#ifdef NUM1344
	char ss[] = "hello world";
	string s(ss);
	cout << s <<endl;
#endif
/*13.45*/
#ifdef NUM1345
	cout << "左值引用是可以绑定左值的引用,右值引用是针对一个将要销毁的对象。不能直接将一个右值引用绑定到变量上。"<<endl;
	int i = 42;
	const int &r3 = i*42; // reference to const (bind to a rvalue)
	int &&rr2 = i*42; // rvalue reference
#endif
/*13.46*/
#ifdef NUM1346
	int f();
	vector<int>vi(100);
	int&& r1 = f();
	int& r2 = vi[0];
	int& r3 = r1;
	int&& r4 = vi[0] * f();
#endif
/*13.47*/
#ifdef NUM1347
	cout << "见Chapter13.h"<<endl;
#endif
/*13.48*/
#ifdef NUM1348
	char text[] = "world";

    String s0;
    String s1("hello");   //not copy construct 
    String s2(s0);     //copy construct
    String s3 = s1;		//copy construct
    String s4(text);  //not copy construct
    s2 = s1;           //copy-assignment

    foo(s1);		//copy construct
    bar(s1);
    foo("temporary");
    bar("temporary");
    String s5 = baz();

    vector<String> svec;
    svec.reserve(8);
    svec.push_back(s0);   //empty string
    svec.push_back(s1);
    svec.push_back(s2);
    svec.push_back(s3);
    svec.push_back(s4);    //"world" 
    svec.push_back(s5);
    svec.push_back(baz());
    svec.push_back("good job");

    for (const auto& s : svec) {
        cout << s.c_str() << endl;
    }
#endif
/*13.49*/
#ifdef NUM1349
	cout<<"见Chapter13.h "<<endl;
#endif
/*13.50*/
#ifdef NUM1350
	String baz()	{
		String ret("world");
		return ret; // first avoided
	}
	String s5 = baz(); // second avoided
#endif
/*13.51*/
#ifdef NUM1351
	unique_ptr<int> clone(int p){
		return unique_ptr<int>(new int(p));   //从int*创建一个unique_ptr<int>
	}
	unique_ptr<int> close(int p){
		unique_ptr<int> ret(new int (p));
		return ret;                          //返回一个局部对象的拷贝
	}
	cout<<"因为函数返回的是一个右值,是一个即将销毁的智能指针。所以,它用了移动赋值操作符,而不是拷贝赋值操作符。"
	"unique_ptr有一个移动构造函数,unique_ptr::unique_ptr(unique_ptr && src);  //参数为一个右值,也就是把函数返回的右值移动到新的unique_ptr上);  "<<endl;
#endif
/*13.52*/
#ifdef NUM1352
	cout<<"rhs参数是拷贝初始化,拷贝初始化类型有拷贝拷贝函数和移动构造函数。左值拷贝右值移动。"
		"由此,hp = hp2; hp2是左值,拷贝构造函数用来拷贝hp2."
		"hp = std::move(hp2);  //移动构造函数移动hp2  "  <<endl;
#endif
/*13.53*/
#ifdef NUM1353
// when used copy-and-swap
 	HasPtr_53 hp1("hello"), hp2("World"), *pH = new HasPtr_53("World");
    hp1 = hp2;
    hp1 = move(*pH);
// when used two assignment operator.

// call constructor
// call constructor
// call constructor
// call copy assignment  !!!
// call move assignment  !!!
// call destructor
// call destructor
#endif
/*13.54*/
#ifdef NUM1354
	cout <<"报错: error: ambiguous overload for'operator=' (operand types are 'HasPtr' and 'std::remove_reference<HasPtr&>::type {aka HasPtr}')  "<<endl;
#endif
/*13.55*/
#ifdef NUM1355
	StrBlob str;
	string s = "hello word";
	str.push_back(s);
	str.print();
#endif
/*13.56*/
#ifdef NUM1356
	cout <<"递归调用,最终导致栈溢出。"<<endl;
#endif
/*13.57*/
#ifdef NUM1357
	cout << "调用移动sorted版本." <<endl;
#endif
/*13.58*/
#ifdef NUM1358
    Foo().sorted(); // call "&&"
    Foo f;
    f.sorted(); // call "const &"
#endif
	return 0;
}

参考资料:

c++ primer中文版第五版,电子工业出版社。

c++ primer第四版习题解答,人民邮电出版社。

pezy@githubhttps://github.com/pezy/CppPrimer





1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:46848次
    • 积分:902
    • 等级:
    • 排名:千里之外
    • 原创:44篇
    • 转载:4篇
    • 译文:0篇
    • 评论:2条
    文章分类