프로그래밍 언어/C/C++

[C++11] Move semantics

소혼 2013. 11. 10. 17:37
반응형

목차로 가기


<개인적으로 공부한 내용들로, 내용이 완벽하지 않을 수 있습니다. 부족한 부분/틀린 부분에 대한 Comment 환영합니다.>


C++11에서는 Move semantics라는 개념이 도입되었습니다.


move semantics가 도입된 배경에 대해 이해하기 위해 아래 예제를 보도록 하겠습니다.

### c++

#include <iostream>
using namespace std;

class Number
{
public:
    explicit Number(int i)
        : m_number(new int(i))
    {
        cout << "Number Constructor is called(" << i << ")" << endl;
    }
    ~Number()
    {
        if (m_number)
            cout << "Number Destructor is called(" << *m_number << ")" << endl;
        delete m_number;
    }
    Number(const Number& rhs)
        : m_number(new int(*rhs.m_number))
    {
        cout << "Number copy constructor is called" << endl;
    }
       
    static void printValue(Number n) {
        cout << "print " << *n.m_number << endl;
    }

private:
    int* m_number;

};

int main()
{
    Number one(1);

    Number::printValue(one);
}

설명을 위해 의도적으로 m_number를 heap에 할당했습니다.


위 예제를 실행하면 아래와 같은 결과가 나옵니다.

Number Constructor is called(1)
Number copy constructor is called
print 1

Number Destructor is called(1)

Number Destructor is called(1)


 printValue의 인자로 넘겨주는 과정에서 한번의 복사과정이 발생하게 됩니다. 불필요한 객체를 막기 위해 printValue를 아래와 같이 바꿀 수 있습니다.

### c++

static void printValue(const Number& n) {
    cout << "print " << *n.m_number << endl;

}

핵심은 reference로 변경한 것입니다. (reference는 원본을 참조하므로 내부에서 수정을 못하도록 const를 붙여주어야 합니다.)

이제 복사 생성자(copy constructor)가 호출되지 않을 것입니다.


이제 예제를 좀 더 확장해 아래와 같은 경우를 보도록 하겠습니다.

### c++

// in main

Number x(one); // (1)

이 경우 x와 one은 별도의 객체이므로 복사 생성자가 불려야 합니다.


### c++

class Number {

    ...

    Number operator+(Number& rhs)
    {
        return Number(m_number + rhs.m_number);
    }

    ...

};


Number make_number()
{
    return Number(2);
}


//in main
Number value(make_number()); // (2)
Number sum(one + two); // (3)

value와 sum을 만들기 위해서는 모두 두번의 복사 생성자가 불립니다.

이 경우에 불리는 복사 생성자는 사실상  불필요합니다. 하지만 제거할 방법은 마땅하지 않습니다.

물론 일부 compiler에서 최적화를 지원합니다. (Return Value Optimization : RVO)

gcc 의 경우 -fno-elide-constructors 를 사용하면 이 기능을 끌 수 있습니다.


현재 예제의 경우는 간단하기 때문에 임시로 생성되는 객체들에 대한 부담이 적을 수 있습니다만, Number가 아니라 Vector와 같은 많은 리소스를 사용해야 하는 경우라면 이것은 그대로 비효율이 됩니다.


(1)과 (2), (3)에는 한가지 중요한 차이점이 있습니다.

(1)은 lvalue를 받은 경우이고, (2), (3)은 rvalue를 넘겨받은 경우라는 사실입니다.


c++11부터는 이동 생성자가 생겼습니다.

rvalue reference를 인자로 받는 새로운 생성자입니다.


위 예제에서 아래와 같이 이동 생성자를 추가하겠습니다.

### c++

    Number(Number&& rhs)
    {
        cout << "Number move constructor is called" << endl;
        m_number = rhs.m_number;
        rhs.m_number = nullptr;
    }

이동 생성자가 복사 생성자와 다른 점은 rvalue reference로 받아서 받은 rhs의 m_number를 그대로 사용한다는 점입니다.

따라서, 복사 생성자가 불렸을 때와 비교해 불필요한 메모리 할당/해제가 사라지게 됩니다.




참조 : http://stackoverflow.com/questions/3106110/what-is-move-semantics

반응형

'프로그래밍 언어 > C/C++' 카테고리의 다른 글

GCC options  (0) 2013.12.05
[C++11] Range based for loop  (4) 2013.11.13
[c++11] rvalue reference  (1) 2013.11.07
[C++11] Variadic template  (0) 2013.11.06
[C++11] unique_ptr  (7) 2013.11.01