음악, 삶, 개발

Move Constructor 와 Move Assignment 에 대한 이해 본문

개발 공부/C++ 약점공략

Move Constructor 와 Move Assignment 에 대한 이해

Lee_____ 2020. 10. 24. 18:59

< Move 는 이삿짐 센터다! > 

C++ 에서 Move 의 의미는 이삿짐 센터라고 외우자.

현실 세계에서 이사는 한 집의 짐을 다른 집으로 다 옮기는것이지만,

C++ 에서는, 한 집에서의 모든 값을 다른 집에 복사해주고, 복사를 마치면 남의 집의 값을 각 type 이 제공하는 방법으로 초기화하는것이다.

예를 들어 완전히 이해해보자.


< C++ 에서 Move 의 의미 >

T 라는 우리가 만든 클래스가 있다.

strcut T {

    int     a;
    double  b;
    float   c;

}

이 클래스 유형을 가진 두개의 객체 A, B 가 있고 A 를 B 로 Move 한다고 가정해보자.

이때 발생하는 실제 프로세스는 아래와 같다.

 

1. 객체 A 의 모든 멤버 a, b, c 의 값을, 객체 B 의 멤버 a, b, c, 의 값으로 각각 복사한다. 

2. 복사가 끝나면 A 의 모든 멤버 a, b, c 를 default 초기화한다. (각 멤버들의 Type 이 제공하는 방식에 따라)


< 컴파일러가 제공해주는 Move Constructor 와 Move Assignment > 

우리가 우리의 클래스안에 Move Constructor 와 Move Assignment 를 따로 정의하지않았을때,

컴파일러가 직접 Move Constructor 와 Move Assignment 를 위의 방식 "원본 복사후, 원본 초기화" 로 정의한다.

대부분의 경우 컴파일러의 선택이 문제가 없지만,

문제가 있는 경우가 있다.

바로 멤버 변수중 const 변수가 있는 경우이다.

struct T {

    const int a;
    double b;

}

위와 같이, 우리는 객체의 멤버 변수중, 초기화 이후에는 절대 변하면 안되는 값을 가지고자할때가 있다.

T 로 와닿지않는다면 다른 예를 들어보자.

struct Note {

    const juce::Uuid id;

}

juce::Uuid 는 객체를 생성했을때 곧 바로 128bits 의 unique 한 id 를 생성해준다.

우리의 각 Note 의 id 는 절대 중복되어서는 안되며, 이 id 가 다른 놈에게 복사되어서도 안된다.

위와 같이 const 멤버인 경우, 당연히 멤버간의 복사는 이루어질수가 없으며,

const 멤버간의 복사를 시도하는 컴파일러가 제공하는 Move Constructor 와 Move assignment 는

당연히 error 이고 컴파일되지않는다.


< const 멤버를 가진 클래스라면, 반드시 우리가 Move 를 정의하자! > 

어떻게 정의할까? 

간단히 클래스를 만들어보자.

struct T {
    
    const int   a {0}
    int         b {0}
    double      c {0.0}
    float       d {0.0f}
    
}

대부분 우리의 클래스 멤버는 위와 같은 모습일것이다.

1개 내지 2개의 멤버만이 const 이고, 나머지는 non-const 인것이다.

이럴때에 const 멤버 a 는 아무런 조취도 취해지면 안되며,

나머지 멤버는 복사후 초기화 과정을 거치면된다.

코드로 보면 아래와같다.

struct T {
    
    // Move Constructor
    T(T&& other) {
        
        // copy other
        b = other.b;
        c = other.c;
        d = other d;
        
        // reset other
        other.b = 0;
        other.c = 0.0
        other.d = 0.0f;
    
    }
    
    // Move Assignment
    T& operator= (T&& other) {
    
        // copy other
        b = other.b;
        c = other.c;
        d = other d;
        
        // reset other
        other.b = 0;
        other.c = 0.0
        other.d = 0.0f;
    
        return *this;
    
    }
    
    const int   a {0}
    int         b {0}
    double      c {0.0}
    float       d {0.0f}
    
}

이것만 기억하라.

 

"복사후 reset"


< Copy Constructor, Copy Assignment vs Move Constructor, Move Assignment >

Copy 녀석들과, 이삿짐센터의 차이를 정리해보자.

 

1. Copy : 인자가 const T& other

2. Move : 인자가 T&& other

3. Copy 는 Copy 만, Move 는 Copy 후 Reset 까지.


< Move Constructor, Move Assignment 를 반드시 정의해야하는 이유 >

앞서 말했지만, 중요해서 다시 남겨놓는다.

나의 멤버 변수들중 const 가 있다면 반드시 Move 를 둘다 정의해야한다.

이 const 멤버를 제외하고 복사후 reset 이 이루어져야하기때문이다.

C++ 이 제공하는 <algorithm> 의 수많은 함수들이 

내부적으로 Move Constructor 와 Move Assignment 를 호출한다.

예를 들어, std::sort() 를 보면 아 Move Constructor 나 Move Assignment 가 호출되겠구나 라고 생각할수있어야한다.


< 마치며 >

나의 멤버 변수중 하나라도 const 가 있다면

곧바로 Copy Constructor, Copy Assignment, Move Constructor, Move Assignment 에 대한 정의를

직접 해야한다. (이 const 멤버 변수를 제외하고 복사가 일어나도록...)