cppPrimer学习13th

cppPrimer学习13th

TODO

13.42

13.1

拷贝构造函数是什么? 什么时候使用它?

拷贝构造函数是 第一个参数是自身的引用类型,且其他参数有默认值
class A
{
	A(const &A);
};

1. 在使用=,右侧会调用拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去
2. 想想什么时候会用到拷贝
	1  返回值非引用,返回对象的时候我们需要拷贝        } 也就是我们需要一个实际对象,传递了一个实际对象
	2. 形参是非引用,传递的是实参                    }
	3. 使用花括号列表初始化数组元素或者聚合类
    4. 容器的`insert,push_back`使用拷贝初始化,`emplace`使用直接初始化,因为他是去调用构造函数的

13.2

Sales_data:Sales_data(Sales_data rhs);

因为我们传递的参数是 Sales_data rhs  那么我们传递了一个参数的时候比如
    Sales_data A;
	Sales_data B=A;

	Sales_data B(Sales_data A)
    {
        内部我们需要先拷贝 A ,也就是执行 Sales_data(A)
            	但是Sales_data(Sales_data A) 本身又需要调用
        		{
            		内部我们需要先拷贝 A ...无限循环
        		}        		
    }
    

13.3

当我们拷贝一个StrBlob的时候,拷贝的是share_ptr指针 ++
	shared_ptr<vector<string>> data;

当我们拷贝一个StrBlobPtr的时候,拷贝的是也是 weak_ptr 指针
	weak_ptr<vector<string>> ptr;

13.4

参数为非引用类型,需拷贝,使用拷贝构造函数
函数的返回类型非引用,也需要进行拷贝,使用拷贝构造函数。
在函数体中arg拷贝到local对象,global拷贝到heap对象,local、*heap拷贝到pa[4]中皆使用拷贝构造函数
local拷贝到*heap为拷贝赋值运算符

13.5

HasPtr(const Hasptr& HP):ps(new string *HP.ps),i(HP.i){}

13.6

1. 拷贝赋值运算符是 对=的重载返回值是该类型对象引用
2. 使用情况,   class A;    A=B, A之前定义过哦
3. 合成拷贝赋值运算完成将 B的非静态成员赋值给A
4. 什么时候生成合成拷贝运算? 我们在情况2的时候,且没有定义自己的合成拷贝的重载

13.7

shared_ptr ++
weak_ptr 不变

13.8

HasPtr& HasPtr::operater=(const HasPtr& A)
{
    delete ps;
    ps=new string(*(A.ps);
    i=A.i;
    return *this;
}

13.9

1. 析构函数是在类被销毁时调用来释放类的资源
2. 合成析构函数释放静态成员,调用成员的析构函数
3. 没有定义自己的析构函数,会有合成的析构函数

13.20

StrBlob 销毁是 shared_ptr -- 如果没有引用,则删除对象
StrBlobPtr 只是销毁自身,不影响str对象

13.21

class HasPtr {
public:
    HasPtr(const std::string& s = std::string()) : ps(new std::string(s)), i(0)
    {
    }
    HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) {}
    HasPtr& operator=(const HasPtr& hp)
    {
        std::string* new_ps = new std::string(*hp.ps);
        delete ps;
        ps = new_ps;
        i = hp.i;
        return *this;
    }
    ~HasPtr() { delete ps; }
private:
    std::string* ps;
    int i;
};

13.12

三次,有一次是 值传递的时候  从实参的 accum拷贝到形参的accum

13.13

// 在使用 vector的时候 push_back 指针的时候拷贝构造了两次  而直接传引用只有一次

void test4()
{

    X *px2 = new X; //只有一个构造,因为是匿名对象
    X x1;

    cout << "use vector" << endl;
    vector<X> vx;
    vx.push_back(x1);   //拷贝构造,一次拷贝构造
    vx.push_back(*px2); //拷贝构造,为什么这里有两次拷贝构造?   一次是传递参数的 形参=*px2,还一次是什么
    //https://stackoverflow.com/questions/30358475/why-the-copy-constructor-is-called-twice-when-doing-a-vector-push-back
    // 没有人规定内部怎么实现的
    //
    cout << "quit" << endl;
    delete px2;

    ////							// vs2015            // gcc
    ////							X()                  // X()
    ////							X()                  // X()
    ////							use vector           // use vector
    ////							X(const X&)          // X(const X&)
    ////							X(const X&)          // X(const X&)
    ////临时对象马上释放了			~X()                 // X(const X&)
    ////							X(const X&)          // ~X()
    ////							quit                 // quit
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()

13.13.2

#include <iostream>
#include <vector>
using namespace std;

struct X
{
    X() { std::cout << "X()" << std::endl; }
    X(const X &) { std::cout << "X(const X&)" << std::endl; }

    // 拷贝赋值
    X &operator=(const X &s) { cout << "X& operator=(const X& s)" << endl; }
    // 析构函数
    ~X() { cout << "~X()" << endl; }
};

void test(const X &ref_x, const X real_x)
{
    X x1, x2; //构造函数
    cout << "I think it will call X(const X &)" << endl;
    X x3(x2); //拷贝构造
    cout << "I think it will call X& operator=(const X& s)" << endl;
    x1 = x2; // 拷贝赋值

    cout << "use by ref" << endl;
    x3 = ref_x;  //拷贝赋值
    x3 = real_x; //拷贝赋值

    X *px2 = new X; //只有一个构造,因为是匿名对象
    cout << "use vector" << endl;
    vector<X> vx;
    vx.push_back(x1);   //拷贝构造
    vx.push_back(*px2); //拷贝构造

    cout << "quit" << endl;
    delete px2;
}

void test2()
{
    X a;
    X b(a);
}

void test3()
{
    X m;
    X *px = new X; //只有一个构造,因为是匿名对象
    cout << "test" << endl;
    test(m, m); // 这里在传递参数的时候先进行了拷贝构造,实参转形参
    delete px;  // 这个不会自动释放的
}

void test4()
{

    X *px2 = new X; //只有一个构造,因为是匿名对象
    X x1;

    cout << "use vector" << endl;
    vector<X> vx;
    vx.push_back(x1);   //拷贝构造,一次拷贝构造
    vx.push_back(*px2); //拷贝构造,为什么这里有两次拷贝构造?   一次是传递参数的 形参=*px2,还一次是什么
    //https://stackoverflow.com/questions/30358475/why-the-copy-constructor-is-called-twice-when-doing-a-vector-push-back
    // 没有人规定内部怎么实现的
    //
    cout << "quit" << endl;
    delete px2;

    ////							// vs2015            // gcc
    ////							X()                  // X()
    ////							X()                  // X()
    ////							use vector           // use vector
    ////							X(const X&)          // X(const X&)
    ////							X(const X&)          // X(const X&)
    ////临时对象马上释放了			~X()                 // X(const X&)
    ////							X(const X&)          // ~X()
    ////							quit                 // quit
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()
    ////							~X()                 // ~X()
}

int main(int argc, char const *argv[])
{
    test4();
    //test2();
    while (1)
        ;
    return 0;
}

13.14

a,b,c 一样的

13.15

会 生成新的id,但是f(b)输出的不是b ,  实参传递会发生再次的拷贝构造

13.16

传递引用不会发生拷贝构造了,这里和const无关哦

13.17

#include <iostream>
using namespace std;

class Num
{
public:
    static int t;
    Num() : me(t++) {}
    Num(const Num &s) : me(s.t++) {}
    int me;
};
int Num::t = 0;

void f(const Num &s)
{
    cout << s.me << endl;
}

int main(int argc, const char **argv)
{
    // 结果是3,4,5
    Num a, b = a, c = b; //0,1,2
    f(a);                // 说明这里发生了拷贝构造,实参传递的时候,如果这里是传递引用,则不会发生拷贝构造,结果是0,1,2
    f(b);
    f(c);
    while (1)
    {
        /* code */
    

    return 0;
}
//3,4,5  // 传递引用是0,1,2

13.19

需要拷贝构造和拷贝赋值,因为ID唯一

13.20

shared_ptr 引用加1

13.21

不需要,因为使用智能指针管理

As synthesized version meet all requirements for this case, no custom version control members need to define. 
Check [#304](https://github.com/Mooophy/Cpp-Primer/issues/304#issuecomment-124081395) for detail.

13.22

/*
13.22 使有指针的类表现为值
*/

#include <iostream>
#include <string>
using namespace std;

class HasPtr
{
private:
    string *ps;
    int i;

public:
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}

    HasPtr(const HasPtr &s) : ps(new string(*(s.ps))), i(s.i) {}

    HasPtr &operator=(const HasPtr &s)
    {
        auto newps = new string(*(s.ps));
        delete ps;
        ps = newps;
        i = s.i;
        return *this;
    }
    ~HasPtr() { delete ps; }

    ostream &print(ostream &c)
    {
        c << "string= " << *ps << endl;
        c << "i= " << i << endl;
        return c;
    }
};

int main(int argc, char const *argv[])
{
    HasPtr A("123");
    HasPtr B("456");
    A.print(cout);
    B.print(cout);
    B = A;
    B.print(cout);
    HasPtr C(A);
    C.print(cout);

    while (1)
        ;
    return 0;
}

13.24

1. 如果未定义析构函数,则内存无法释放
2. 如果未定义拷贝构造,则同一个内存被两个指针指向,在析构的时候被释放两次 

13.25

拷贝赋值 可以使用智能指针直接指向,
    拷贝构造 也可以直接使用智能指针赋值 
也可以开辟新的内存空间
    
因为使用智能指针,自己会释放,所以不需要析构

13.26

    // 拷贝构造函数
    StrBlob(const StrBlob &src) : data(make_shared<vector<string>>(*src.data)) {}
    //拷贝赋值
    StrBlob &operator=(const StrBlob &src)
    {
        auto newptr = make_shared<vector<string>>(*src.data);
        data = newptr;
        return *this;
    }
/*
12.19 
12.20 读入文件 使用这个类

一个 StrBlob类,底层数据是指向 vector<string> 的指针
一个 StrBlob_ptr类,可以认为是StrBlob类的指针类

定义  StrBlob类,数据成员是 vector<string> 的shared_ptr
        begin  指向第一个元素的 StrBlob_ptr 也就是cur=0?
        end                   最后一个元素的指针cur=size()   end是开区间,所以不用减1
定义  StrBlob_ptr 类,指向 StrBlob类中的数据成员 *shared_ptr
        一个指向 vector<string> 的指针,带有小标
        *      获得string
        ++     将下标++


*/

#include <vector>
#include <string>
#include <memory>
#include <initializer_list>
#include <exception>
#include <iostream>
#include <fstream>

using namespace std;

class StrBlob_ptr;

class StrBlob
{
    using Blob_size_type = vector<string>::size_type;

    friend class StrBlob_ptr;

private:
    /* data */
    shared_ptr<vector<string>> data;

public:
    StrBlob() : data(make_shared<vector<string>>()) {}
    StrBlob(initializer_list<string> li) : data(make_shared<vector<string>>(li)) {}
    // 拷贝构造函数
    StrBlob(const StrBlob &src) : data(make_shared<vector<string>>(*src.data)) {}
    //拷贝赋值
    StrBlob &operator=(const StrBlob &src)
    {
        auto newptr = make_shared<vector<string>>(*src.data);
        data = newptr;
        return *this;
    }

    StrBlob_ptr begin(); //{ return StrBlob_ptr(*this); }
    StrBlob_ptr end();   //{ return StrBlob_ptr(*this, data->size()); }

    Blob_size_type size() const
    {
        return data->size();
    }

    bool empty() const
    {
        return data->empty();
    }

    bool check(Blob_size_type has, const string &msg) const
    {
        if (has >= data->size())
            throw std::out_of_range(msg);
    }

    void push_back(string &s)
    {
        data->push_back(s);
    }
    void pop_back()
    {
        check(0, "pop_back");
        data->pop_back();
    }

    string &front()
    {
        check(0, "front");
        return data->front();
    }

    string &back()
    {
        check(0, "back");
        return data->back();
    }

    const string &front() const
    {
        check(0, "front");
        return data->front();
    }

    const string &back() const
    {
        check(0, "back");
        return data->back();
    }

    ~StrBlob() {}
};

class StrBlob_ptr
{
private:
    // 底层指针指向的内容是 shared_ptr,可能存在释放的情况
    weak_ptr<vector<string>> ptr;
    int cur;
    shared_ptr<vector<string>> check(int size, const string msg) const;

public:
    // 构造函数,指向空的ptr
    StrBlob_ptr() : cur(0) {}
    StrBlob_ptr(StrBlob &s, int offset = 0) : ptr(s.data), cur(offset) {}
    ~StrBlob_ptr() {}

    // * 解引用
    string &deref() const;
    StrBlob_ptr &increase_ref();
    // 同时需要定义!= 这里应该还要判断 p的指向是否相同
    bool operator!=(const StrBlob_ptr &p) { return p.cur != cur; }
};

shared_ptr<vector<string>> StrBlob_ptr::check(int size, const string msg) const
{
    auto ptr_get = ptr.lock();
    if (!ptr_get)
        throw runtime_error(msg);
    if (size >= ptr_get->size())
        throw out_of_range(msg);
    return ptr_get;
}

string &StrBlob_ptr::deref() const
{
    auto ptr_get = check(cur, "get shared_ptr");
    return (*ptr_get)[cur];
}

StrBlob_ptr &StrBlob_ptr::increase_ref()
{
    check(cur, "get shared_ptr");
    cur++;
    return *this;
}

/*
 *   这两个函数一定要放到 StrBlob_ptr 这个类下面定义,因为用到了StrBlob_ptr具体的构造函数; 即使最开始已经声明了
 *    
 *
 *
*/
StrBlob_ptr StrBlob::begin()
{
    return StrBlob_ptr(*this);
}
StrBlob_ptr StrBlob::end()
{
    return StrBlob_ptr(*this, data->size());
}

int main(int argc, const char **argv)
{

    StrBlob csb{"1", "2", "3"};
    StrBlob sb{"A1", "A2", "A3"};

    StrBlob newCopy(csb);
    std::cout << newCopy.front() << " " << newCopy.back() << std::endl;

    newCopy = sb;
    std::cout << newCopy.front() << " " << newCopy.back() << std::endl;

    // std::cout << csb.front() << " " << csb.back() << std::endl;
    // sb.back() = "change";
    // std::cout << sb.front() << " " << sb.back() << std::endl;

    // cout << "vector string :" << endl;
    // for (StrBlob_ptr ch = sb.begin(); ch != sb.end(); ch.increase_ref())
    //     cout << ch.deref() << ",";
    // cout << endl;

    // ifstream ifile("./12.19.cpp");
    // StrBlob fs_blob;
    // for (string str; getline(ifile, str);)
    //     fs_blob.push_back(str);

    // cout << "file string :" << endl;
    // for (StrBlob_ptr ch = fs_blob.begin(); ch != fs_blob.end(); ch.increase_ref())
    //     cout << ch.deref() << endl;

    while (1)
        ;
    return 0;
}

13.27

/*
13.27 使有指针的类表现为指针
*/

#include <iostream>
#include <string>
using namespace std;

class HasPtr
{
private:
    string *ps;
    int i;
    size_t *use;

public:
    HasPtr(const string &s = string()) : ps(new string(s)), i(0), use(new size_t(1)) {}

    //HasPtr(const HasPtr &s) : ps(new string(*(s.ps))), i(s.i) {}
    // 拷贝构造,
    HasPtr(const HasPtr &s) : use(s.use), ps(s.ps), i(s.i)
    {
        ++*use;
    }

    HasPtr &operator=(HasPtr &s)
    {
        ++*s.use;
        if (--*use == 0)
        {
            delete ps;
            delete use;
        }
        ps = s.ps;
        use = s.use;
        i = s.i;

        return *this;
    }
    //~HasPtr() { delete ps; }
    ~HasPtr()
    {
        if (--*use == 0)
        {
            delete ps;
            delete use;
        }
    }

    ostream &print(ostream &c)
    {
        c << "string= " << *ps << endl;
        c << "i= " << i << endl;
        return c;
    }
};

int main(int argc, char const *argv[])
{
    {
        // HasPtr A("123");
        // HasPtr B("456");
        // A.print(cout);
        // B.print(cout);
        // B = A;
        // B.print(cout);
        // HasPtr C(A);
        // C.print(cout);

        HasPtr A1("333");
        HasPtr A3("444");
        HasPtr A2(A1);

        A1.print(cout);
        A2.print(cout);
        A2 = A3;
        A2.print(cout);

    }

    while (1)
        ;
    return 0;
}

13.29

类型不一致,内部会依次调用 swap(std::string*, std::string*)  等

13.30

/*
13.30 增加swap
13.22 使有指针的类表现为值
*/

#include <iostream>
#include <string>
using namespace std;

class HasPtr
{
private:
    string *ps;
    int i;

public:
    friend void swap(HasPtr &a, HasPtr &b);
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}

    HasPtr(const HasPtr &s) : ps(new string(*(s.ps))), i(s.i) {}

    HasPtr &operator=(const HasPtr &s)
    {
        auto newps = new string(*(s.ps));
        delete ps;
        ps = newps;
        i = s.i;
        return *this;
    }
    ~HasPtr() { delete ps; }

    ostream &print(ostream &c)
    {
        c << "string= " << *ps << endl;
        c << "i= " << i << endl;
        return c;
    }
};

void swap(HasPtr &a, HasPtr &b)
{
    cout << "-------swap -----" << endl;
    swap(a.i, b.i);
    swap(a.ps, b.ps);
}

int main(int argc, char const *argv[])
{
    HasPtr A("123");
    HasPtr B("456");
    swap(A, B);

    A.print(cout);
    B.print(cout);

    while (1)
        ;
    return 0;
}

13.31

/*
13.30 增加swap
13.22 使有指针的类表现为值
*/
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

class HasPtr
{
private:
    string *ps;
    int i;

public:
    friend void swap(HasPtr &a, HasPtr &b);
    friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}

    HasPtr(const HasPtr &s) : ps(new string(*(s.ps))), i(s.i) {}

    HasPtr &operator=(HasPtr s)
    {
        cout << "operator=" << endl;
        this->swap(s);
        return *this;
    }
    void swap(HasPtr &s)
    {
        using std::swap;
        swap(ps, s.ps);
        swap(i, s.i);
        cout << "-------swap -----" << *s.ps << "<<>><<>>" << *ps << endl;
    }
    ~HasPtr() { delete ps; }

    ostream &print(ostream &c)
    {
        c << "string= " << *ps << endl;
        c << "i= " << i << endl;
        return c;
    }
};

void swap(HasPtr &a, HasPtr &b)
{
    cout << "-------swap -----" << *a.ps << "<<>><<>>" << *b.ps << endl;

    swap(a.i, b.i);
    swap(a.ps, b.ps);
}

bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
    return *lhs.ps < *rhs.ps;
}

int main(int argc, char const *argv[])
{
    HasPtr A("7");
    HasPtr B("3");
    HasPtr C("9");

    vector<HasPtr> vs;
    vs.push_back(A);
    vs.push_back(B);
    vs.push_back(C);
    vs.push_back(HasPtr("1"));
    for (auto i : vs)
        i.print(cout);
    sort(vs.begin(), vs.end());
    for (auto i : vs)
        i.print(cout);

    while (1)
        ;
    return 0;
}

13.32

不会受益,因为是指针的交换不涉及到内存的拷贝

13.33

因为需要修改Folder的set集合

13.35

如果message使用合成的拷贝控制函数,那么没有办法更新floder的set

13.38

1. 没有动态分配内存
2. 使用交换的话 是形如  operator=(Message a) //参数不是引用,是拷贝
	这里的交换对于a本身也要交换,也就是a自身也要处理, 这里做不到的  可以参见p519

13.34

13.36

13.37

/*
    设计自己的message 类和 Folder 类

    message 可以被多个Folder 所包含 ,一个Folder 包含多个 message
    Message 拷贝构造的时候,需要复制folder,同时往folder添加
    Message 拷贝赋值的时候,需要执行删除自身之前节点的操作 同时添加到新的Folder中


*/

#include <string>
#include <vector>
#include <iostream>
#include <set>
using namespace std;

class Folder;

class Message
{
    friend class Folder;
    friend void swap(Message &a, Message &b);

private:
    string Id; // fow swap debug,这个id是为了显示交换结果,因为交换message的书本做法是全部交换,而不是单单string name
    string msg_raw;
    set<Folder *> folder_list;

public:
    Message(const string &msg = "") : msg_raw(msg), Id(msg) {}
    Message(const Message &s);
    Message &operator=(const Message &s);

    ~Message();

    // 添加消息到Folder
    void add_to_folder(Folder &f);
    void del_from_folder(Folder &f);
    ostream &print(ostream &c)
    {
        c << msg_raw << "--ID=" << Id;
        return c;
    }
};

class Folder
{
private:
    string name;
    set<Message *> msg_list;

public:
    Folder(const string &name = "") : name(name) {}
    ~Folder();

    void add_msg(Message &msg);
    void del_msg(Message &msg);
    print(ostream &c)
    {
        c << "Dir:" << name << endl;
        for (auto it : msg_list)
            it->print(c) << endl;
    }
};
//-------------------------------------Message-------------------------------------------
//
//
//
//
void Message::add_to_folder(Folder &f)
{
    folder_list.insert(&f);
    f.add_msg(*this);
}
void Message::del_from_folder(Folder &f)
{
    folder_list.erase(&f);
    f.del_msg(*this);
}
// 在自己的set中的Folder中的,执行删除消息
Message::~Message()
{
    for (auto f : folder_list)
        f->del_msg(*this);
    // 自身元素没有指针类型,无需释放
}
// 拷贝构造函数, 1是复制Message本身,2是添加到Folder中
Message::Message(const Message &s) : msg_raw(s.msg_raw), folder_list(s.folder_list)
{
    for (auto i : s.folder_list)
        i->add_msg(*this);
}
// 拷贝赋值,需要注意自身赋值, 0.删除自身的Folder 指向 1. 复制原来的消息体,添加到Folder  2. 如果是自身的话
Message &Message::operator=(const Message &s)
{
    if (this == &s)
        return *this;
    for (auto f : folder_list)
        f->del_msg(*this);
    msg_raw = s.msg_raw;
    folder_list = s.folder_list;
    for (auto f : folder_list)
        f->add_msg(*this);
    return *this;
}

//-----------------------------------Folder----------------------------------------------
//
//
//
//
void Folder::add_msg(Message &msg)
{
    msg_list.insert(&msg);
}
void Folder::del_msg(Message &msg)
{
    msg_list.erase(&msg);
}
// 在自己的set中的message中的,执行删除
Folder::~Folder()
{
    for (auto i : msg_list)
        i->del_from_folder(*this);
    // 自身元素没有指针类型,无需释放
}

//------------------------------------------------------------------------------------------
void swap(Message &a, Message &b)
{
    //cout << "*****************use swap************" << endl;
    for (auto f : a.folder_list)
        f->del_msg(a);
    for (auto f : b.folder_list)
        f->del_msg(b);
    swap(a.msg_raw, b.msg_raw); //我觉得交换消息应该只需要交换字符串?
    swap(a.folder_list, b.folder_list);
    for (auto f : a.folder_list)
        f->add_msg(a);
    for (auto f : b.folder_list)
        f->add_msg(b);
}
//-------------------------------------------------------------------------------------------
//
//
//
//
int main(int argc, char const *argv[])
{
    // Folder root("root");
    // /* code */
    // {
    //     Message A("msg_A");
    //     Message B("msg_B");
    //     Message C("msg_C");
    //     Message D("msg_D");
    //     A.add_to_folder(root);
    //     B.add_to_folder(root);
    //     C.add_to_folder(root);
    //     D.add_to_folder(root);
    //     root.print(cout);
    //     B.del_from_folder(root);
    //     root.print(cout);
    // }
    // // 执行msg的析构,删除消息
    // root.print(cout);

    Folder root("root");
    Folder root2("root2");
    Message Q("msg_Q");
    Q.add_to_folder(root2);
    root2.print(cout);
    {
        Message A("msg_A");
        Message B("msg_B");
        Message C(A);
        Message D("msg_D");
        A.add_to_folder(root);
        B.add_to_folder(root);
        C.add_to_folder(root);
        D.add_to_folder(root);
        root.print(cout);
        D = B;
        root.print(cout);
        A = A;
        root.print(cout);

        cout << "-----swap----" << endl;
        swap(A, Q);
        root.print(cout);
        root2.print(cout);
    }
    root.print(cout);

    while (1)
        ;
    return 0;
}

13.38

因为操作不一致

13.39

13.40

/*
    使用 allocator 实现自己的 vector<string>
13.39
    resize	重设容器大小以容纳 count 个元素。若当前大小大于 count ,则减小容器为其首 count 个元素。
    reserve 增加 vector 的容量到大于或等于 new_cap 的值 若 new_cap 大于当前的 capacity() ,则分配新存储,否则该方法不做任何事。reserve() 不更改 vector 的 size 
13.40
    增加一个 initiallizer_list 的构造函数
*/

#include <string>
#include <memory>
#include <utility>
#include <iostream>
#include <initializer_list>
using std::cin;
using std::cout;
using std::endl;
using std::initializer_list;
using std::pair;
using std::string;

class StrVec
{
private:
    string *elements;   //首元素
    string *first_free; //最后一个元素之后的元素,也就是end
    string *cap;        //实际容量之后的一个元素

public:
    StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
    StrVec(const StrVec &);
    StrVec(initializer_list<string> li);
    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 new_cap);
    void resize(size_t count);
    void resize(size_t count, const std::string &);

private:
    std::allocator<string> alloc;
    void reallocate();
    void chk_n_alloc()
    {
        if (size() == capacity())
            reallocate();
    }
    void free();
    pair<string *, string *> allc_n_copy(const string *, const string *); // 从迭代器拷贝,返回首指针和尾指针
};

void StrVec::push_back(const string &s)
{
    chk_n_alloc();
    alloc.construct(first_free++, s); // 先复制,再++
}

pair<string *, string *> StrVec::allc_n_copy(const string *b, const string *e)
{
    auto data = alloc.allocate(e - b);
    return {data, std::uninitialized_copy(b, e, data)}; //指向最后复制的元素后一元素的迭代器
}

void StrVec::free()
{
    if (elements)
    {
        for (auto i = first_free; i != elements;)
        {
            alloc.destroy(--i); //先--到有效内存
        }
        alloc.deallocate(elements, cap - elements);
    }
}

StrVec::StrVec(const StrVec &s)
{
    auto data = allc_n_copy(s.begin(), s.end());
    elements = data.first;
    first_free = cap = data.second;
}

StrVec::StrVec(initializer_list<string> li)
{
    auto newdata = allc_n_copy(li.begin(), li.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}
StrVec::~StrVec()
{
    free();
}
StrVec &StrVec::operator=(const StrVec &s)
{
    auto data = allc_n_copy(s.begin(), s.end());
    free();
    elements = data.first;
    first_free = cap = data.second;
    return *this;
}

void StrVec::reallocate()
{
    auto newcap = size() ? 2 * size() : 1;
    auto data = alloc.allocate(newcap);
    auto dst = data;
    auto elm = elements;
    for (size_t i = 0; i != size(); ++i)
    {
        alloc.construct(dst++, std::move(*elm++)); // 从老空间拷贝
    }
    free(); //释放老元素,这里的elements 没有改变
    elements = data;
    first_free = dst;
    cap = elements + newcap; //起始地址加容量
}
//reserve 增加 vector 的容量到大于或等于 new_cap 的值 若 new_cap 大于当前的 capacity() ,
//则分配新存储,否则该方法不做任何事。reserve() 不更改 vector 的 size
void StrVec::reserve(size_t new_cap)
{
    if (new_cap > capacity())
    {
        auto data = alloc.allocate(new_cap);
        auto dst = data;
        auto elm = elements;
        for (size_t i = 0; i != size(); ++i)
        {
            alloc.construct(dst++, std::move(*elm++)); // 从老空间拷贝
        }
        free(); //释放老元素,这里的elements 没有改变
        elements = data;
        first_free = dst;
        cap = elements + new_cap; //起始地址加容量
    }
}
/*
resize重设容器大小以容纳 count 个元素。若当前大小大于 count ,则减小容器为其首 count 个元素。
*/
void StrVec::resize(size_t count, const std::string &s)
{
    int old_size = size();
    if (size() > count)
    {
        for (auto i = first_free; size() != count;)
        {
            alloc.destroy(--i); //先--到有效内存,会调用存储对象的析构函数
            first_free--;
        }
    }
    else if (size() < count)
    {
        //初始化构造
        reserve(count);
        for (; size() != count;)
            push_back(s);
    }
}

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

int main(int argc, char const *argv[])
{
    StrVec s = {"A", "B"};
    s.push_back("1");
    s.push_back("2");
    s.resize(5, "****");
    s.resize(4);

    StrVec s2(s);
    StrVec s3;
    s3.push_back("3");
    s3 = s2;

    StrVec vv[3] = {s, s2, s3};
    int j = 0;
    for (auto sv : vv)
    {
        cout << "[ " << j++ << " ]"
             << ": ";
        for (auto i = sv.begin(); i != sv.end(); i++)
        {
            cout << *i << " ";
        }
        cout << endl;
    }

    while (1)
        ;
    return 0;
}

13.41

push_back 中使用  alloc.construct(first_free++, s); // 先复制,再++ 
如果 使用  alloc.construct(++first_free, s); // 先++ 再复制,那么第一个为空了

13.43

void StrVec::free()
{
    if (elements)
    {
        // for (auto i = first_free; i != elements;)
        // {
        //     alloc.destroy(--i); //先--到有效内存
        // }
        // 捕获了this,这里的alloc就是 this->alloc
        for_each(elements, first_free, [this](std::string &rhs) { alloc.destroy(&rhs); });
        alloc.deallocate(elements, cap - elements);
    }
}

@Mooophy: The new version is better. Compared to the old one, 
it doesn't need to worry about the order and decrement.So more straightforward and handy. 
    The only thing to do for using this approach is to add "&" to build the pointers to string pointers.

13.44

/**
 * 实现自己的string
 * 
 */

#include <memory>
#include <utility>
#include <algorithm>
#include <iostream>

class String
{
private:
    char *elem;
    char *end;
    std::allocator<char> alloc;

    std::pair<char *, char *> alloc_n_copy(const char *, const char *);
    void range_initializer(const char *, const char *);
    void free();
    /* data */
public:
    friend std::ostream &operator<<(std::ostream &o, String &s);
    String() : elem(nullptr), end(nullptr) {}
    String(const char *);
    String(const String &);
    ~String();
    String &operator=(const String &);
};

std::pair<char *, char *> String::alloc_n_copy(const char *b, const char *e)
{
    auto str = alloc.allocate(e - b);
    return std::make_pair(str, std::uninitialized_copy(b, e, str));
}
void String::range_initializer(const char *b, const char *e)
{
    auto ret = alloc_n_copy(b, e);
    elem = ret.first;
    end = ret.second;
}

String::String(const char *s)
{
    char *sl = const_cast<char *>(s);
    while (*sl)
        ++sl;
    range_initializer(s, ++sl);
}
String::String(const String &s)
{
    std::cout << "copy-construct" << std::endl;
    range_initializer(s.elem, s.end);
}
void String::free()
{
    //std::cout << "free" << std::endl;
    if (elem)
    {
        //std::cout << "free elem" << std::endl;
        std::for_each(elem, end, [this](char &s) { alloc.destroy(&s); });
        alloc.deallocate(elem, end - elem);
    }
}

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

String &String::operator=(const String &s)
{

    // auto newstr = alloc_n_copy(rhs.elem, rhs.end);
    // free();
    // elem = newstr.first;
    // end = newstr.second;
    // std::cout << "copy-assignment" << std::endl;
    // return *this;

    if (&s == this)
        return *this;
    range_initializer(s.elem, s.end);
    std::cout << "copy-assignment" << std::endl;
    return *this;
}

std::ostream &operator<<(std::ostream &o, String &s)
{
    return o << s.elem;
}

int main(int argc, char const *argv[])
{
    {
        String sa("abcdefg");
        std::cout << sa << std::endl;

        String sb = sa;
        std::cout << sb << std::endl;

        String sc;
        sc = sa;
        std::cout << sc << std::endl;
    }

    while (1)
        ;
    return 0;
}

13.45

右值引用绑定到右值,也就是将要销毁的对象

13.46

//什么类型的引用可以绑定到下面的初始化器上
#include <vector>
using namespace std;

int f()
{
    return 0;
}
int main(int argc, char const *argv[])
{
    vector<int> vi(100);
    int &&r1 = f();
    int &r2 = vi[0];
    int &r3 = r1;
    int &&r4 = vi[0] * f();
    return 0;
}

13.47

13.48

这里现象很奇怪,应该是vector内部扩充

    {
        std::vector<String> vs;
        vs.reserve(100);-------------------------------加上这句话就只有一次拷贝
        String a("123");
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;

        String b("123");
        vs.push_back(b);
        std::cout << __LINE__ << std::endl;
        vs.push_back(b);
        std::cout << __LINE__ << std::endl;
    }

    // 127
    // copy-construct
    // 129
    // copy-construct
    // copy-construct
    // 131
    // copy-construct
    // copy-construct
    // copy-construct
    // 133
    // copy-construct
    // 137
    // copy-construct
    // copy-construct
    // copy-construct
    // copy-construct
    // copy-construct
    // 139
		  
/**
 * 13.48  定义一个vector<String> 查看push_back复制了几次
 * 
 * 13.44实现自己的string
 * 
 */

#include <memory>
#include <utility>
#include <algorithm>
#include <iostream>
#include <vector>

class String
{
private:
    char *elem;
    char *end;
    std::allocator<char> alloc;

    std::pair<char *, char *> alloc_n_copy(const char *, const char *);
    void range_initializer(const char *, const char *);
    void free();
    /* data */
public:
    friend std::ostream &operator<<(std::ostream &o, String &s);
    String() : elem(nullptr), end(nullptr) {}
    String(const char *);
    String(const String &);
    ~String();
    String &operator=(const String &);
};

std::pair<char *, char *> String::alloc_n_copy(const char *b, const char *e)
{
    auto str = alloc.allocate(e - b);
    return std::make_pair(str, std::uninitialized_copy(b, e, str));
}
void String::range_initializer(const char *b, const char *e)
{
    auto ret = alloc_n_copy(b, e);
    elem = ret.first;
    end = ret.second;
}

String::String(const char *s)
{
    char *sl = const_cast<char *>(s);
    while (*sl)
        ++sl;
    range_initializer(s, ++sl);
}
String::String(const String &s)
{
    std::cout << "copy-construct" << std::endl;
    range_initializer(s.elem, s.end);
}
void String::free()
{
    //std::cout << "free" << std::endl;
    if (elem)
    {
        //std::cout << "free elem" << std::endl;
        std::for_each(elem, end, [this](char &s) { alloc.destroy(&s); });
        alloc.deallocate(elem, end - elem);
    }
}

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

String &String::operator=(const String &s)
{

    // auto newstr = alloc_n_copy(rhs.elem, rhs.end);
    // free();
    // elem = newstr.first;
    // end = newstr.second;
    // std::cout << "copy-assignment" << std::endl;
    // return *this;

    if (&s == this)
    {
        std::cout << "copy-assignment self!!!!!!" << std::endl;
        return *this;
    }

    range_initializer(s.elem, s.end);
    std::cout << "copy-assignment" << std::endl;
    return *this;
}

std::ostream &operator<<(std::ostream &o, String &s)
{
    return o << s.elem;
}

int main(int argc, char const *argv[])
{
    if (0)
    {
        /*常规构造*/
        String sa("abcdefg");
        std::cout << sa << std::endl;

        /*自赋值*/
        sa = sa;
        String *p = &sa;
        sa = *p;
        std::cout << sa << std::endl;

        /*拷贝构造*/
        String sb = sa;
        std::cout << sb << std::endl;

        /*拷贝赋值*/
        String sc;
        sc = sa;
        std::cout << sc << std::endl;
    }

    {
        std::vector<String> vs;
        //vs.reserve(100);        //-------------------------------加上这句话就只有一次拷贝
        String a("123");
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;
        vs.push_back(a);
        std::cout << __LINE__ << std::endl;

        String b("123");
        vs.push_back(b);
        std::cout << __LINE__ << std::endl;
        vs.push_back(b);
        std::cout << __LINE__ << std::endl;
    }

    // 127
    // copy-construct
    // 129
    // copy-construct
    // copy-construct
    // 131
    // copy-construct
    // copy-construct
    // copy-construct
    // 133
    // copy-construct
    // 137
    // copy-construct
    // copy-construct
    // copy-construct
    // copy-construct
    // copy-construct
    // 139

    while (1)
        ;
    return 0;
}

13.49.1

//从m 中移动信息,并释放m自身的消息
void Message::move_Folder(Message *m)
{
    folder_list = std::move(m->folder_list);
    for (auto f : folder_list)
    {
        f->del_msg(*m);
        f->add_msg(*this);
    }
    m->folder_list.clear(); //m原本的析构
}

Message::Message(Message &&s) noexcept : msg_raw(std::move(s.msg_raw)), Id(std::move(s.Id))
{
    std::cout << " **move construct**" << std::endl;
    move_Folder(&s);
}
Message &Message::operator=(Message &&s) noexcept
{
    std::cout << " **move operator=**" << std::endl;
    if (this == &s)
        return *this;
    msg_raw = std::move(s.msg_raw);
    Id = std::move(s.Id);
    move_Folder(&s);
    return *this;
}

13.49.2

String::String(String &&s) noexcept
{
    std::cout << "move-construct" << std::endl;

    elem = s.elem;
    end = s.end;
    alloc = s.alloc;
    s.elem = s.end = nullptr;
}

String &String::operator=(String &&s) noexcept
{
    std::cout << "move operator =" << std::endl;

    if (this == &s)
        return *this;

    elem = s.elem;
    end = s.end;
    alloc = s.alloc;
    s.elem = s.end = nullptr;
    return *this;
}

13.49.3

StrVec::StrVec(StrVec &&s)
{
    elements = s.elements;
    first_free = s.first_free;
    cap = s.cap;
    alloc = s.alloc;
    s.elements = s.first_free = s.cap = nullptr;
}

StrVec &StrVec::operator=(StrVec &&s)
{
    if (this == &s)
        return *this;
    elements = s.elements;
    first_free = s.first_free;
    cap = s.cap;
    alloc = s.alloc;
    s.elements = s.first_free = s.cap = nullptr;
    return *this;
}

13.50

    std::cout << __LINE__ << std::endl;
    std::vector<String> vs;
    vs.reserve(100);
    String a("a");
    String b("b");
    std::cout << __LINE__ << std::endl;
    vs.push_back(a);
    std::cout << __LINE__ << std::endl;
    vs.push_back(std::move(b));
    std::cout << __LINE__ << std::endl;
    vs.push_back(String("move"));

13.51

为啥以下的 unique指针可以拷贝?
因为用的是移动拷贝,也就是原来的unique失去了控制权,转交给返回的unique_ptr了

unique_ptr<int> clone(int p) {
    // ok: explicitly create a unique_ptr<int> from int*
    return unique_ptr<int>(new int(p));
}

13.51

详细解释p478的HasPtr对象赋值时发生了什么?
   特别是 一步步描述 hp,hp2以及HasPtr赋值运算符中的参数rhs的值发生了什么变化

class HasPtr
{
    HasPtr(HasPtr && p) noexcept :ps(p.ps),i(p.i){p.ps=0;}
    HasPtr& operator=(HasPtr rhs)
    {
        swap(*this,rhs);return *this;
    }
}
    

赋值:
	1. 值传递,首先调用拷贝构造或者移动构造函数,根据实参类型
        
hp=hp2;					//拷贝赋值
hp=std::move(hp2);		//移动构造--> 然后析构失效的实参

13.53

从底层效率看,HasPtr的赋值运算并不理想?

因为参数是实参,在调用移动构造的时候还需要swap没有必要

HasPtr& operator=(HasPtr &&p) noexcept
{
	if(&p==this) return *this;
	ps=p.ps;
	i=p.i;
	p.ps=0;
	return *this;
}

13.54

如果我们定义了移动赋值,但没有改变拷贝交换,发生了什么?
    // add move
    HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) { p.ps = 0; }
    HasPtr &operator=(HasPtr s)
    {
        cout << "operator=" << endl;
        this->swap(s);
        return *this;
    }
    HasPtr &operator=(HasPtr &&p) noexcept
    {
        if (&p == this)
            return *this;
        ps = p.ps;
        i = p.i;
        p.ps = 0;
        return *this;
    }

程序报错了,提示 candidate: HasPtr& HasPtr::operator=(HasPtr&&)  也就是重载失败,无法区分 二义性
    
    

13.54.2

/*
13.30 增加swap
13.22 使有指针的类表现为值
13.53 增加移动构造 和移动赋值
*/
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

class HasPtr
{
private:
    string *ps;
    int i;

public:
    friend void swap(HasPtr &a, HasPtr &b);
    friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}

    HasPtr(const HasPtr &s) : ps(new string(*(s.ps))), i(s.i)
    {
        std::cout << "call copy construct" << std::endl;
    }

    // add move
    HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i)
    {
        std::cout << "call move construct" << std::endl;
        p.ps = 0;
    }
    // HasPtr &operator=(HasPtr s)
    // {
    //     cout << "operator=" << endl;
    //     this->swap(s);
    //     return *this;
    // }
    HasPtr &operator=(HasPtr &s)
    {
        auto newp = new std::string(*s.ps);
        delete ps;
        ps = newp;
        i = s.i;
        std::cout << "call copy assignment" << std::endl;
        return *this;
    }
    HasPtr &operator=(HasPtr &&p) noexcept
    {
        std::cout << "call move assignment" << std::endl;
        if (&p == this)
            return *this;
        ps = p.ps;
        i = p.i;
        p.ps = 0;
        return *this;
    }

    void swap(HasPtr &s)
    {
        using std::swap;
        swap(ps, s.ps);
        swap(i, s.i);
        cout << "-------swap -----" << *s.ps << "<<>><<>>" << *ps << endl;
    }
    ~HasPtr() { delete ps; }

    ostream &print(ostream &c)
    {
        c << "string= " << *ps << endl;
        c << "i= " << i << endl;
        return c;
    }
};

void swap(HasPtr &a, HasPtr &b)
{
    cout << "-------swap -----" << *a.ps << "<<>><<>>" << *b.ps << endl;

    swap(a.i, b.i);
    swap(a.ps, b.ps);
}

// HasPtr &operator=(HasPtr &s)
// {
//     swap(*this, s);
// }

// // HasPtr &operator=(HasPtr &&p) noexcept
// // {
// // }

bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
    return *lhs.ps < *rhs.ps;
}

int main(int argc, char const *argv[])
{
    HasPtr A("7");
    HasPtr B("3");
    HasPtr C("9");

    B = A;
    B = std::move(C);

    // vector<HasPtr> vs;
    // vs.push_back(A);
    // vs.push_back(B);
    // vs.push_back(C);
    // vs.push_back(HasPtr("1"));
    // for (auto i : vs)
    //     i.print(cout);
    // sort(vs.begin(), vs.end());
    // for (auto i : vs)
    //     i.print(cout);

    while (1)
        ;
    return 0;
}

13.55

void push_back(string &&s)
{
    std::cout << "psh_back:&&" << std::endl;
    data->push_back(std::move(s));
}

13.56

13.57

13.58

/*
    13.56 Foo 右值引用调用的问题
    13.57 无限递归
    13.58 const版本的调用一次&& 后成功
*/
#include <iostream>
#include <algorithm>
#include <vector>
using std::vector;
class Foo
{
private:
    vector<int> data;

public:
    ~Foo(){};

    push_back(int s) { data.push_back(s); }
    std::ostream &print(std::ostream &o)
    {
        for (auto c : data)
            o << c << ",";
        return o << std::endl;
    }
    Foo sorted() &&;      //用于可以改变的右值
    Foo sorted() const &; // 用于任何类型的Foo
};

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

// 常规版本
// Foo Foo::sorted() const &
// {
//     std::cout << "use const &" << std::endl;
//     Foo ret(*this);
//     std::sort(ret.data.begin(), ret.data.end());
//     return ret;
// }

// // 无限递归
// Foo Foo::sorted() const &
// {
//     std::cout << "use const &" << std::endl;
//     Foo ret(*this);
//     //std::sort(ret.data.begin(), ret.data.end());
//     return ret.sorted();
// }

//  调用&& 一次 后正常输出
Foo Foo::sorted() const &
{
    std::cout << "use const &" << std::endl;
    //std::sort(ret.data.begin(), ret.data.end());
    return Foo(*this).sorted();
}

int main(int argc, const char **argv)
{
    Foo f1;
    f1.push_back(3);
    f1.push_back(1);
    f1.push_back(2);
    Foo f2 = f1.sorted();
    f2.print(std::cout);

    Foo f3 = f2;
    std::move(f3).sorted();
    f3.print(std::cout);

    while (1)
        ;
    return 0;
}
posted @ 2020-04-11 14:50  zongzi10010  阅读(147)  评论(0编辑  收藏  举报