ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 1. 이동 의미론(Move Semantics)
    Modern C++/Move Semantics 2020. 6. 29. 12:38

    1. 이동 의미론 

    C++11 이후 C++ 의 가장 중요한 변화 중 하나는 복사를 통한 자원의 낭비를 줄이기 위해 객체 자원의 소유권을 이전 할 수 있는 이동 의미론이 추가되었다는 점이다. 

     

    class Foo {

    };

    위와 같이 Foo 클래스 정의 시 컴파일러는 자동으로 기본 생성자, 복사 생성자, 할당 연산자, 소멸자를 생성해 주는 데, C++11 이후에 는 여기에 이동 생성자, 이동 할당 연산자가 추가되었다.

    또한 STL 라이브러리의 범용 유틸리티나, 컨테이너들에도 이동 생성자나, 이동 할당 연산자가 구현되어 복사로 인한 쓸데 없는 자원의 소비를 줄일 수 있게 되었다. 

     

    이동 생성자와 이동 할당 연산자는 아래와 같은 형태로 std::vector<T>의 예를 통해 확인 할 수 있다. 

    std::vector<T, Allocator>::vector  이동 생성자(move constructor)
    vector( vector&& other );                                                                                      (since C++11)
                                                                                                                                  (until C++17)
    vector( vector&& other ) noexcept;                                                                      (since C++17)
    std::vector<T,Allocator>::operator = 이동 할당 연산자 (move assignment operator)
    vector& operator = ( vector&& other );                                                                    (since C++ 11)
                                                                                                                                     (until C++ 17)
    vector& operator = ( vector&& other) noexcept;                                                      (since C++17)

    2. 왼쪽 값(L-value) 와 오른쪽 값(R-Value) 

    이동 연산을 이해하기 위해서는 먼저 오른쪽 값과 왼쪽 값에 대해 구분할 수 있어야 한다.

     

    왼쪽값

    왼쪽값은

    1. 기본적으로 할당 연산자(=)의 왼쪽에 위치 가능한 값으로 변수가 가장 대표적이다. 

    int a = 5; // a는 왼값(l-value)

     

    2. 주소를 취할 수 있다. 

    int *ptr_a = &a // a의 주소를 취할 수 있다.

    3. 왼값 참조에 바인딩 될 수 있다. 

    int &ref_a = a; 

    그 외 가능한 왼값 

    struct address {

        std::string street, city, potal_code;

    };

    address addr;

    addr.city = "Seoul"; // 객체의 멤버 변수도 왼쪽 값

    int& get_count() {

        static int count_ = 0;

        return count_; 

    }

    get_count() = 10;  //왼값 참조를 반환하는 함수 호출도 왼쪽

    오른쪽 값

    오른쪽 값은 

    1. 할당 연산자(=)의 왼쪽에 위치할 수 없다. 

    int get_numeric( return 5; }

    //오른 값은 절대 할당 연산자의 왼쪽에 있을 수 없다. 
    0 = a; 
    get_numeric() = 5;  

    2. 주소를 취할 수 없다.

    //오른 값의 주소는 취할 수 없다.
    &5; 
    &get_numeric();

    3. 왼값 참조에 바인딩 될 수 없다. 

    //오른 값은 왼값 참조에 바인드 될 수 없다. 
    int& l_ref0 = 5;
    int& l_ref1 = get_numeric();

    4. C++11 부터 소개된 오른 값 참조에 바인딩 될 수 있다. 

    int&& r_ref0 = 5;
    int&& r_ref1 = get_numeric();


    기본적으로 오른쪽 값은 임시 객체로 해당 객체를 구분할 구별자가 없어 주소나 참조 할 수 없다. 

     

    std::vector 내 이동 생성자와 복사 생성자 비교

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    std::vector<int> convert_container(int start, int end) {
        std::vector<int> v;
        v.reserve( end - start + 1);
        for(int i = start; i <= end; i++) {
            v.push_back(i);
        }
        return v;
    }
    
    void print_vector(const vector<int>& v) {
        std::cout << "{ ";
        for (int i = 0; i < v.size(); i++) {
            if(i != v.size() -1) {
                std::cout << v[i] << ", " ;
            } else {
                std::cout << v[i] << " ";
            }
        }
        std::cout << "}\n";
    }
    
    std::vector<int> return_lvalue(int start, int end) {
        std::vector<int> v;
        v.reserve(end - start + 1);
        for(int i = start; i <= end; i++) {
            v.push_back(i);
        }
        return v;
    }
    
    int main() {
        std::vector<int> v1{1, 2, 3, 4, 5};
        auto v2 = v1;  // v1은 r-value 이므로 복사 생성자 호출
        //v2 객체를 생성하기 위해 추가적인 복사 생성자가 호출된다. 
        
        print_vector(v1);
        print_vector(v2);
    
        auto v3 = return_lvalue(1, 5); // return_lvalue 함수는 l-value를 반환하므로 이동 연산자 호출
        //return_lvalue 에서 생성된 임시 객체의 리소스의 소유권을 v3로 이전
        //v3 생성 시 이동 생성자 호출로 v3 내 추가적인 복사는 이루어지지 않는다. 
        
        print_vector(v3);
        return 0;
    }

    위와 같이 C++11 이 후 C++ 표준 컨테이너나 유틸리티는 이동 연산을 지원해 복사 최소화로 인한 성능 개선을 할 수 있도록 지원하고 있다. 

    'Modern C++ > Move Semantics' 카테고리의 다른 글

    5. C++11 객체 생성 규칙 Rule of Five  (0) 2020.07.01
    4. 이동가능 객체(Movable Type)  (0) 2020.06.30
    3. 완벽 전달(Perfect Forwarding)  (1) 2020.06.29
    2. std::move  (0) 2020.06.29

    댓글

Designed by Tistory.