'이동 생성자' 개념을 다루기 전에 'r-value' 개념을 먼저 잡아야합니다.

간단히 'r-value'는 단순 대입 연산자의 오른쪽 항을 말합니다. 코드를 보겠습니다.

void main() {
    int a = 3 + 4; // '3 + 4'를 계산해 'a' 변수에 대입합니다.
    int&& b = 3 + 4; // '3 + 4' 자체를 'b' 변수가 참조합니다.
    b++;
}

 

'r-value'란 개념은 이전부터 있던 개념입니다. 하지만 'r-value를 참조한다'라는 개념은 C++11에 와서 생겨났습니다. 

왜 'r-value 참조'가 필요한지 다음 코드를 보겠습니다.

void Func(int& param) { /* do somthing */ }

void main() {
    int a = 10;
    
    Func(a); 
    // Func(a + 10);  - 컴파일 에러가 발생합니다.
    // Func(10 + 20); - 마찬가지로 컴파일 에러가 발생합니다.
}

 

Func() 함수의 매개변수를 '참조자'가 아닌 일반 변수로 받는다면 실행되겠지만 그러면 '복사 오버헤드'가 발생합니다.  이런 식으로 함수를 사용할거라면 애초에 C++를 사용할 이유가 없습니다. 이때, 'r-value 참조'를 사용하면 간단히 해결됩니다. 위의 Func() 함수를 수정해보겠습니다.

void Func(int&& param) { /* do somthing */ }

 

지금까지 살펴봤던 'r-value 참조' 개념을 '생성자'에 적용시킨게 '이동 생성자'입니다.

class MyData {
private:
    int m_nData = 0;
    
public:
    MyData() { /* do somthing */ }
    
    // 복사 생성자
    MyData(const MyData& rhs) : m_nData{ rhs.m_nData } { /* do somthing */ }
    
    // 이동 생성자
    MyData(const MyData&& rhs) : m_nData{ rhs.m_nData } { /* do somthing */ }
    
    ~MyData() { /* do somthing */ }
    
    MyData& operator=(const MyData&) = default;
    
    int GetData() const { return m_nData; }
    
    void SetData(int nParam) { m_nData = nParam; }
}

MyData Func(int nParam) {
    MyData a;
 
    return a;
}

void main() {
    MyData myData;
    
    myData = Func(10);
}

Func() 함수가 반환하는 MyData 객체는 '이름없는 임시 객체(r-value)'입니다. 어차피 myData 객체에 대입한 후 사라질 객체이므로 '복사 오버헤드'를 통해 '생성자'를 호출하면 쓸데없이 성능만 잡아먹게됩니다. 그래서 'r-value 참조'를 통해 '생성자'를 호출한 후 이것을 myData 객체에 대입합니다.