码迷,mamicode.com
首页 > 编程语言 > 详细

【足迹C++primer】47、Moving Objects(1)

时间:2014-07-18 11:16:47      阅读:286      评论:0      收藏:0      [点我收藏+]

标签:move   移动构造函数   移动赋值运算符   

Moving Objects(1)

* 功能:Moving Objects
* 时间:2014年7月17日08:46:45
* 作者:cutter_point
*/

#include<iostream>
#include<string>
#include<memory>
#include<utility>
#include<vector>
#include<algorithm>
#include<set>
#include<map>


using namespace std;
/**************************************
Rvalue References
**************************************/
void fun1()
{
    int i=42;
    int &r=i;   //引用,r引用了i
//    int &&rr=i; //错误,不能右值引用一个值
//    int &r2=i*42;   //错误,i*42是一个右值
    const int &r3=i*42; //ok,我们可愿意绑定一个右值在const上
    int &&rr2=i*42;     //吧rr2绑定到结果上
    //个人理解就是&单个是引用别名,而&&也是引用但是是const结果
}
/**************************************
Lvalues Persist; Rvalues Are Ephemeral
左值长久,右值一瞬
**************************************/
/*
Rvalue references refer to objects that are about to be destroyed. Hence, we
can “steal” state from an object bound to an rvalue reference.
*/

/**************************************
Variables Are Lvalues
**************************************/
void fun2()
{
    int &&rr1=42;   //正确,这&&rr1是右值,不能是变化的
//    int &&rr2=rr1;  //错误由于rr1是一个左值,不能引用为右值
}

/*
A variable is an lvalue; we cannot directly bind an rvalue reference to a
variable even if that variable was defined as an rvalue reference type
*/

/**************************************
The Library move Function
**************************************/
void fun3()
{
    int &&rr1=42;   //正确,这&&rr1是右值,不能是变化的
    int &&rr2=std::move(rr1);  //用move吧这rr1转换成左值
}
/*
We can destroy a moved-from object and can assign a new value to it, but
we cannot use the value of a moved-from object.
Warning:
Code that uses move should use std::move, not move. Doing so avoids
potential name collisions.
*/

/**************************************
13.6.2. Move Constructor and Move Assignment
**************************************/

/**
Move Operations, Library Containers, and Exceptions
*/
class StrVec
{
public:
    StrVec():elements(nullptr), first_free(nullptr), cap(nullptr){}
    StrVec(const StrVec&);
    StrVec(StrVec&&) noexcept;
    StrVec & operator=(const StrVec&);
    StrVec & operator=(StrVec&&) noexcept;
    ~StrVec();

    string* begin() const {return elements;}            //起始指针
    string* end() const {return first_free;}            //第一个为空的元素
    void push_back(const string&);  //拷贝元素进入队列
    size_t size() const {return first_free-elements;}   //队列存放元素个数
    size_t capacity() const {return cap-elements;}      //队列存储空间大小

private:
    allocator<string> alloc;
    string* elements;
    string* first_free;
    string* cap;
    pair<string*, string*> alloc_n_copy(const string*, const string*);
    void free();
    void reallocate();  //重新分配空间并且把原来的元素移动到新空间上
    void chk_n_alloc()
    {
        if(size() == capacity())        //判断数据长度是否已经达到分配空间的上限
            reallocate();       //如果是,那么就重新分配空间
    }

};

/**************************************
Move Iterators
**************************************/
void StrVec::reallocate()
{
    //分配新空间
    auto newcapacity=size() ? 2*size() : 1;
    auto first=alloc.allocate(newcapacity);

    auto last=uninitialized_copy(make_move_iterator(begin()), make_move_iterator(end()), first);
    free();
    elements=first;
    first_free=last;
    cap=elements+newcapacity;
}

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

inline
pair<string*, string*>
StrVec::alloc_n_copy(const string* b, const  string* e) //开始和结尾指针
{
    //要拷贝对象要求的空间大小
    auto data=alloc.allocate(e-b);
    //吧b,e之间的元素拷贝到data中
    //返回一个pair
    pair<string*, string*> p={data, uninitialized_copy(b, e, data)};
    return p;
}

/*
void StrVec::reallocate()
{
    //直接把当前容量扩充到2倍
    auto newcapacity=size() ? 2*size() : 1 ;
    //allocate新内存
    auto newdata=alloc.allocate(newcapacity);   //申请新空间
    //吧元素重老的地方移到新的地方
    auto dest=newdata;      //指出新空间第一个空位置
    auto elem=elements;     //老队列的第一个元素
    //全部构造到新的里面去
    for(size_t i=0 ; i != size() ; ++i)
        alloc.construct(dest++, std::move(*elem++));    //循环吧老的元素移动到新的上
    free();     //移完了,把老空间全部释放

    //重新更新数据指针
    elements=newdata;
    first_free=dest;
    cap=elements+newcapacity;
}
*/

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



StrVec::StrVec(StrVec &&s) noexcept     //这个就像移动的是内存空间,然后把指针为空就可以了
        :elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    s.elements=s.first_free=s.cap=nullptr;
}

/**
Move-Assignment Operator
*/

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

StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{
    //直接进行赋值,不用特别考虑自己给自己赋值了
    if(this != &rhs)    //不是自己,不然不做操作
    {
        free(); //清空this内存
        elements=rhs.elements;
        first_free=rhs.first_free;
        cap=rhs.cap;

        //让rhs重置
        rhs.elements=rhs.first_free=rhs.cap=nullptr;
    }

    return *this;
}

/**************************************
A Moved-from Object Must Be Destructible
**************************************/
/*
Warning
After a move operation, the “moved-from” object must remain a valid,
destructible object but users may make no assumptions about its value.
*/

/**
The Synthesized Move Operations
*/
struct X
{
    int i;
    string s;
};

struct hasX
{
    X mem;
};

void fun4()
{
    X x, x2=std::move(x);
    hasX hx, hx2=std::move(hx);
}

//这些是当类没有自己定义自己的移动构造函数和移动赋值运算符的时候
//编译器会自动调用编译器自带的函数

/**
让默认的move构造函数和move赋值运算符失效
*/
struct Y
{
    int i;
    string s;
};
struct hasY
{
    hasY()=default;
    hasY(hasY&&)=default;
    Y mem;
};

void fun5()
{
    hasY hy, hy2=std::move(hy);     //错误:移动构造函数已经被删除了
}

/*
Note
Classes that define a move constructor or move-assignment operator must
also define their own copy operations. Otherwise, those members are deleted
by default.
就是定义了那个move的构造之后,也要定义它的operator操作,不然就会被default覆盖删除掉
*/

/**
Rvalues Are Moved, Lvalues Are Copied ...
*/
StrVec getVec(istream &)   //这个函数返回一个右值
{
    StrVec v1;
    return v1;
}

void fun6()
{
    StrVec v1, v2;
    v1=v2;      //v2是一个左值,这是拷贝
    v2=getVec(cin);             //这个里面getVec得到的是右值,move
}

/**
...But Rvalues Are Copied If There Is No Move Constructor
右值也可能是拷贝,如果只是申明move构造函数,而没有定义
*/
class Foo
{
public:
    Foo()=default;
    Foo(const Foo&);    //copy构造函数
};

Foo::Foo(const Foo &f)
{

}

void fun7()
{
    Foo x;
    Foo y(x);       //拷贝构造x是一个左值
    Foo z(std::move(x));    //这里还是拷贝,因为没有move构造函数
}

/**
Copy-and-Swap Assignment Operators and Move
*/
class HasPtr {
	friend void swap(HasPtr&, HasPtr&);
public:
    HasPtr(const std::string &s = std::string()):
		ps(new std::string(s)), i(0) { }

	// each HasPtr  has its own copy of the string to which ps points
    HasPtr(const HasPtr &p):
		ps(new std::string(*p.ps)), i(p.i) { }

    HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) {p.ps=0;}

//	HasPtr& operator=(const HasPtr &);
	//包含赋值操作和交换操作的赋值运算符
	HasPtr& operator=(HasPtr rhs){swap(*this, rhs); return *this;}
	//这里注意,HasPtr的参数不是引用,而是一个拷贝参数,对于左值来说是拷贝
	//但是对于右值来说这就是一个move操作,那么这个操作符既包含copy赋值运算符
	//和move赋值运算符功能

	~HasPtr() { delete ps; }
private:
    std::string *ps;
    int    i;
};

inline
void swap(HasPtr &lhs, HasPtr &rhs)
{
	using std::swap;
	swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
	swap(lhs.i, rhs.i);   // swap the int members
}

/*
using std::string;
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
	string *newp = new string(*rhs.ps);  // copy the underlying string
	delete ps;       // free the old memory
	ps = newp;       // copy data from rhs into this object
	i = rhs.i;
	return *this;    // return this object
}
*/
HasPtr f(HasPtr hp)  // HasPtr passed by value, so it is copied
{
	HasPtr ret = hp; // copies the given HasPtr
	// process ret
	return ret;      // ret and hp are destroyed
}

void fun8()
{
    HasPtr hp, hp2;
    hp=hp2;             //这个是拷贝,由于hp2是左值
    hp=std::move(hp2);  //这个就是move赋值运算了
    //自动识别!!!
}

/**************************************
Advice: Updating the Rule of Three
All five copy-control members should be thought of as a unit: Ordinarily, if a
class defines any of these operations, it usually should define them all. As
we’ve seen, some classes must define the copy constructor, copy-assignment
operator, and destructor to work correctly . Such classes
typically have a resource that the copy members must copy. Ordinarily,
copying a resource entails some amount of overhead. Classes that define the
move constructor and move-assignment operator can avoid this overhead in
those circumstances where a copy isn’t necessary.
总的来说就是要定义三个函数:
以前是:拷贝构造函数,拷贝赋值运算符,析构函数
现在就要:拷贝构造函数,移动构造函数,移动拷贝赋值运算符,析构函数
**************************************/

/**************************************
Move Operations for the Message Class
**************************************/
class Folder;

class Message {
	friend void swap(Message&, Message&);
	friend class Folder;
public:
    // folders is implicitly initialized to the empty set
    explicit Message(const string &str = ""):
		contents(str) { }

    // copy control to manage pointers to this Message
    Message(const Message&);             // copy constructor
    //移动构造函数,利用右值move
    Message(Message&&);
    void move_Folders(Message *);             //吧m中的Folders指针move操作到Message上
    Message& operator=(const Message&);  // copy assignment
    //移动赋值运算符
    Message& operator=(Message&&);
    ~Message();                          // destructor

    // add/remove this Message from the specified Folder's set of messages
    void save(Folder&);
    void remove(Folder&);
    void debug_print(); // print contents and it's list of Folders,
                        // printing each Folder as well
private:
    string contents;      // actual message text
    set<Folder*> folders; // Folders that have this Message

    // utility functions used by copy constructor, assignment, and destructor
    // add this Message to the Folders that point to the parameter
    void add_to_Folders(const Message&);

    // remove this Message from every Folder in folders
    void remove_from_Folders();

    // used by Folder class to add self to this Message's set of Folder's
    void addFldr(Folder *f) { folders.insert(f); }
    void remFldr(Folder *f) { folders.erase(f); }
};
// declaration for swap should be in the same header as Message itself
void swap(Message&, Message&);

class Folder {
	friend void swap(Message&, Message&);
	friend class Message;
public:
    ~Folder(); // remove self from Messages in msgs
    Folder(const Folder&); // add new folder to each Message in msgs
    Folder& operator=(const Folder&); // delete Folder from lhs messages
                                      // add Folder to rhs messages
    Folder() { }        // defaults ok

    void save(Message&);   // add this message to folder
    void remove(Message&); // remove this message from this folder

    void debug_print(); // print contents and it's list of Folders,
private:
    set<Message*> msgs;  // messages in this folder

    void add_to_Messages(const Folder&);// add this Folder to each Message
    void remove_from_Msgs();     // remove this Folder from each Message
    void addMsg(Message *m) { msgs.insert(m); }
    void remMsg(Message *m) { msgs.erase(m); }
};

//move赋值运算符
Message& Message::operator=(Message &&rhs)
{
    if(this != &rhs)
    {
        remove_from_Folders();
        contents=std::move(rhs.contents);
        move_Folders(&rhs);
    }

    return *this;
}

//move构造函数
Message::Message(Message &&m):contents(std::move(m.contents))
{
    move_Folders(&m);
}

// move the Folder pointers from m to this Message
void Message::move_Folders(Message *m)
{
    folders=std::move(m->folders);
    //移完指针再把数据移过去
    for(auto f : folders)
    {
        f->remMsg(m);
        f->addMsg(this);
    }

    m->folders.clear();
}

void swap(Message &lhs, Message &rhs)
{
	using std::swap;  // not strictly needed in this case, but good habit

	// remove pointers to each Message from their (original) respective Folders
	for (set<Folder*>::iterator f = lhs.folders.begin();
			f != lhs.folders.end(); ++f)
		(*f)->remMsg(&lhs);
	for (set<Folder*>::iterator f = rhs.folders.begin();
			f != rhs.folders.end(); ++f)
		(*f)->remMsg(&rhs);

	// swap the contents and Folder pointer sets
	swap(lhs.folders, rhs.folders);   // uses swap(set&, set&)
	swap(lhs.contents, rhs.contents); // swap(string&, string&)

	// add pointers to each Message to their (new) respective Folders
	for (set<Folder*>::iterator f = lhs.folders.begin();
			f != lhs.folders.end(); ++f)
		(*f)->addMsg(&lhs);
	for (set<Folder*>::iterator f = rhs.folders.begin();
			f != rhs.folders.end(); ++f)
		(*f)->addMsg(&rhs);
}

Message::Message(const Message &m):
    contents(m.contents), folders(m.folders)
{
    add_to_Folders(m); // add this Message to the Folders that point to m
}

Message& Message::operator=(const Message &rhs)
{
	// handle self-assignment by removing pointers before inserting them
    remove_from_Folders();    // update existing Folders
    contents = rhs.contents;  // copy message contents from rhs
    folders = rhs.folders;    // copy Folder pointers from rhs
    add_to_Folders(rhs);      // add this Message to those Folders
    return *this;
}

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

// add this Message to Folders that point to m
void Message::add_to_Folders(const Message &m)
{
	for (set<Folder*>::iterator f = m.folders.begin();
			f != m.folders.end(); ++f) // for each Folder that holds m
        (*f)->addMsg(this); // add a pointer to this Message to that Folder
}

// remove this Message from the corresponding Folders
void Message::remove_from_Folders()
{
	for (set<Folder*>::iterator f = folders.begin();
			f != folders.end(); ++f)  // for each pointer in folders
		(*f)->remMsg(this);    // remove this Message from that Folder
	folders.clear();        // no Folder points to this Message

}

void Folder::add_to_Messages(const Folder &f)
{
	for (set<Message*>::iterator msg = f.msgs.begin();
			msg != f.msgs.end(); ++msg)
		(*msg)->addFldr(this);   // add this Folder to each Message
}

Folder::Folder(const Folder &f) : msgs(f.msgs)
{
    add_to_Messages(f);  // add this Folder to each Message in f.msgs
}

Folder& Folder::operator=(const Folder &f)
{
    remove_from_Msgs();  // remove this folder from each Message in msgs
	msgs = f.msgs;       // copy the set of Messages from f
    add_to_Messages(f);  // add this folder to each Message in msgs
    return *this;
}

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


void Folder::remove_from_Msgs()
{
    while (!msgs.empty())
        (*msgs.begin())->remove(*this);
}
void Message::save(Folder &f)
{
    folders.insert(&f); // add the given Folder to our list of Folders
    f.addMsg(this);     // add this Message to f's set of Messages
}

void Message::remove(Folder &f)
{
    folders.erase(&f); // take the given Folder out of our list of Folders
    f.remMsg(this);    // remove this Message to f's set of Messages
}

void Folder::save(Message &m)
{
    // add m and add this folder to m's set of Folders
    msgs.insert(&m);
    m.addFldr(this);
}

void Folder::remove(Message &m)
{
    // erase m from msgs and remove this folder from m
    msgs.erase(&m);
    m.remFldr(this);
}

void Folder::debug_print()
{
    cerr << "Folder contains " << msgs.size() << " messages" << endl;
    int ctr = 1;
    for (set<Message*>::iterator m = msgs.begin();
			m != msgs.end(); ++m) {
        cerr << "Message " << ctr++ << ":\n\t" << (*m)->contents << endl;
	}
}

void Message::debug_print()
{
    cerr << "Message:\n\t" << contents << endl;
    cerr << "Appears in " << folders.size() << " Folders" << endl;
}




int main()
{

    return 0;
}


PS:代码全在,里面各种注释,我一后还是这样写吧,除了一些特殊的感悟自己写出来,然后大部分大家直接在代码中学习,这样避免了,看那些文字的烦躁,和那些看文字的无聊!!!
今天早上出去溜达一圈,回来早餐吃泡面,尼玛,我真是服了,第一次早上吃泡面,然后把面什么的放碗里,最后把调料又放回了袋子里,我的天!!!

【足迹C++primer】47、Moving Objects(1),布布扣,bubuko.com

【足迹C++primer】47、Moving Objects(1)

标签:move   移动构造函数   移动赋值运算符   

原文地址:http://blog.csdn.net/cutter_point/article/details/37921671

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!