목차로 가기


<개인적으로 공부한 내용들로, 내용이 완벽하지 않을 수 있습니다. 부족한 부분/틀린 부분에 대한 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] Move semantics  (5) 2013.11.10
[c++11] rvalue reference  (1) 2013.11.07
[C++11] Variadic template  (0) 2013.11.06
[C++11] unique_ptr  (7) 2013.11.01
  1. 2016.02.04 12:10 신고

    일단 오타부터 고쳐주시길;

    • 소혼 2016.02.11 14:14 신고

      오타가 있는 줄 몰랐네요.

      꼼꼼히 읽어보겠습니다.
      혹시 수정이 안되어 있다면, 어떤 부분이 오타인지 알려주시면 좀 더 도움이 될 것 같습니다.

    • 우현 2016.08.15 23:36 신고

      어이가 없네 남이 써논거 검색해서 눈팅이나 하는 주제에
      올려주면 고마울 일이지 말을 그렇게 밖에 못할까
      말하는 뽄새를 보니까 오타따지기 전에 인성부터 고쳐야 할듯...ㅉㅉ

      소혼님 성품이 겁나젠틀하시네요 저는 아니라서 죄송

  2. 지나가다 2016.04.05 04:38 신고

    첫 소스에 printValue 함수가 객체를 받고, 다음 부분에서 레퍼런스를 받는걸로 수정되는게 문맥이 맞는데 첫 소스에 이미 레퍼런스를 받게 되어있는게 오타가 아닐까 싶네요...

    아뭏든, 잘 보고 갑니다~

  3. 궁그미 2018.08.02 23:54 신고

    Number sum(one + two); // (3)

    여기서 two 가

    Number value(make_number()); // (2)

    value 가 되어야 하는 오타가 아닐듯 싶네용..

+ Recent posts

티스토리 툴바