c++ primer(第五版)學習筆記及習題答案代碼版(第十三章)拷貝控制

筆記較爲零散,都是自己不熟悉的知識點。

習題答案至於一個.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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章