Friday, May 31, 2013

C++11 Standard Explained: 4. Move Semantic

One of the best new feature introduced to C++11 was the move semantic. We can now make explicit references and optimize for rvalues. (I will not go on to explain the details about rvalues here. If you want a detailed explanation, please refer to this link).

To show you an example of move semantic in effect, we experiment by starting with the following class:

#include 
#include 

using namespace std;

class Foo {
  public:
    Foo(int cnt, char * ptr) {
        _content = cnt;
        _pContent = new char [strlen(ptr)];
        strcpy(_pContent, ptr);
        cout << "initializing the object" << endl;
        cout << _pContent << endl;
    }
  private:
    int _content;
    char * _pContent;
};

int main() {
    char * str = "Hello World!"; 
    Foo foo(10, str);
}   

if we run the program, we would get
[leorex@localhost move_semantic]$ ./a.out
initializing the object
Hello World!

Easy enough. Now let a function initialize Foo, and we will return the result to main Here I will use the example of integer pointer just to make the code shorter
#include 

using namespace std;

class Foo {
  public:
    Foo(int * cnt) {
        _content = new int;
        *_content = *cnt; 
        cout << "initializing the object" << endl;
    }
    Foo() {
        _content = new int;
        *_content = 0; 
        cout << "Running default constructor" << endl;
    }
    Foo & operator=(const Foo & rhs) {
        cout << "call the copy assignment" << endl;
        if(_content == NULL)
            _content = new int;
        *_content = *rhs._content;
    }
    // destructor
    ~Foo() {
        cout << "destroying an object" << endl;
        if (_content != NULL)
            delete _content;
    }
    Foo (const Foo & rhs) {
        cout << "call the copy constructor" << endl;
        if(_content == NULL)
            _content = new int;
        *_content = *rhs._content;
    }
  private:
    int *_content;
};

Foo init() {
    int i = 20;
    Foo foo(&i);
    return foo;
}

int main() {
    int i = 10;
    Foo foo = init();
    Foo bar(&i);
    swap(foo, bar);
}
Running result involves a lot of calling to copy constructor, which is hardly surprising
[leorex@localhost move_semantic]$ ./03
initializing the object
initializing the object
call the copy constructor
call the copy assignment
call the copy assignment
destroying an object
destroying an object
destroying an object

With the move constructor and move assignment operator, things are different
#include 

using namespace std;

class Foo {
  public:
    Foo(int * cnt) {
        _content = new int;
        *_content = *cnt; 
        cout << "initializing the object" << endl;
    }
    Foo() {
        _content = new int;
        *_content = 0; 
        cout << "Running default constructor" << endl;
    }
    Foo & operator=(Foo&& rhs) {
        cout << "call the move assignment" << endl;
        _content = rhs._content;
        rhs._content = NULL;
    }
    // destructor
    ~Foo() {
        cout << "destroying an object" << endl;
        if (_content != NULL)
            delete _content;
    }
    // move constructor
    Foo (Foo && rhs) {
        cout << "call the move constructor" << endl;
        _content = rhs._content;
        rhs._content = NULL;
    }
  private:
    int *_content;
};

Foo init() {
    int i = 20;
    Foo foo(&i);
    return foo;
}

int main() {
    int i = 10;
    Foo foo = init();
    Foo bar(&i);
    swap(foo, bar);
}
Result shows that swap only involves moving the pointers around.
[leorex@localhost move_semantic]$ ./11
initializing the object
initializing the object
call the move constructor
call the move assignment
call the move assignment
destroying an object
destroying an object
destroying an object