[C++] 이동 생성자 / 이동 연산자

Date:     Updated:

카테고리:

태그:

이 글은 C++의 이동 생성자와 이동 대입자을 공부하고 정리한 글입니다.
약한 포인터는 원시 포인터의 해제에 영향을 끼치지 않는 포인터로 강한 참조 카운터가 0이 될 때 소멸된다.

lvalue

  • 단일 식을 넘어 지속되는 개체가
    • 주소가 있음
    • 이름이 있는 변수
    • const 변수
    • 배열 변수
    • 비트 필드(bit-fields)
    • 공용 구조체(unions)
    • 클래스 멤버
    • 좌측 값 참조(&)로 반환하는 함수 호출
    • 문자열 리터럴

    코드 예시

    int number = 10;                      // number는 lvalue 
    const int NAME_MAX = 20;              // NAME_MAX는 lvalue
    int* numberPtr = &number;             // numberPtr는 lvalue 
    
    std::map<std::string, float> scoreMap;
    scoreMap["Lulu"] = 60.f;              // scoreMap["Lulu"]는 lvalue
    
    Person person;
    person.Name = "Lulu";                 // person.Name은 lvalue
    
    const char* name = "Gragas";          // name과 "Gragas"는 lvalue
    

rvalue

  • lvalue가 아닌 개체
  • 사용되는 다인 식을 넘어 지속되지 않는 일시적인 값
    • 주소가 없는 개체
    • 리터럴(문자열 리터럴 제외)
    • 참조로 반환하지 않는 함수 호출
    • i++와 i–(++i와 –i는 lvalue)
    • 기본으로 지원되는 산술식, 논리식, 그리고 비교식
    • 열거형(enum)
    • 람다(lambda)

    코드 예시

    int number = 10;                      // number는 lvalue 
    10 = number;                          // 에러. 10은 rvalue이기 때문에
    (number + 1) = 20;                    // 에러. (number + 1)은 rvalue이기 때문에
    
    int anotherNumber = 20;
    int result = number + anotherNumber;  // (number + anotherNumber)는 rvalue
    
    &number = 20;                         // 에러. &number은 rvalue이기 때문에
    
    int number1 = 10;
    int number2 = 20;
    
    if (number1 < number2)                // (number1 < number2)는 rvalue
    {
      // ...
    }
    
    float CalculateAverage()
    {
      float average;
      // ...
      return average;
    }
    
    int number = 10;
    int&& rNumber = number;                   // 에러, number는 lvalue
    int&& rNumber1 = 10;                      // OK, 10는 rvalue
    float&& rAverage = CalculateAverage();    // OK, CalculateAverage()는 rvalue
    

과거 C++의 문제 (C++11 이전)

  • 복사가 2번 된다
    • 복사 1 : 함수에서 리턴될 때 반환 값을 임시 개체로 복사
    • 복사 2 : 복사된 임시 개체에서 실제 변수로 복사

rvalue 참조(&&)

  • C++11 이후에 새로 나온 연산자
  • 기능상 & 연산자와 비슷
  • & 연산자는 lvalue 참조에 사용
  • && 연산자는 rvalue 참조에 사용

std::move()

  • 개념상으로는 소유권이전
  • rvalue 참조를 반환
  • 실제 내부에서 도는 방식은 lvalue를 rvalue로 변환

이동 생성자

<class_name>::<class_name>(<class_name>&&)
  • 다른 개체 멤버 변수들의 소유권을 가져 옴
  • 복사 생성자와 달리, 메모리 재할당을 하지 않음
  • 복사 생성자보다 빠름
  • 약간 얕은 복사와 비슷
MyString::MyString(MyString&& other)
    : mString(other.mString)
    , mSize(other.mSize)
{
    other.mString = nullptr;
    other.mSize = 0;
}

이동 대입 연산자

<class_name>& <class_name>::operator=(<class_name>&&)
  • 이동 생성자와 같은 개념
  • 다른 개체 멤버 변수들의 소유권을 가져 옴
  • 이것도 메모리 재할당을 하지 않음
  • 얕은 복사
MyString& MyString::operator=(MyString&& other)
{
    if (this != &other)
    {
        delete[] mString;

        mString = other.mString;
        mSize = other.mSize;

        other.mString = nullptr;
        other.mSize = 0;
    }
}

STL 컨테이너용 이동 문법

  • C++11 이후로, STL 컨테이너에 이동 생성자와 이동 대입이 생김
  • 그래서, 그것들을 따로 구현할 필요가 없음

rvalue 최적화

  • 이동 생성자와 이동 대입 연산자
    • 아직 유효
  • 포인터 대신 개체 자체를 반환하는 함수
    • 함수에서 rvalue를 반환하는 것은 실제 매우 느림
    • 반환 값 최적화(Return Value Optimization)라고 하는 컴파일러 최적화를 깨뜨림

베스트 프랙티스

  • 기본적으로 그냥 개체를 반환
  • 더 빨라진다고 입증된 경우에만 함수가 rvalue를 반환하도록 바꾸자


참조

포큐아카데미 C++ 언매니지드 프로그래밍



💻 열심히 공부해서 작성 중이니 오류나 틀린 부분이 있을 경우 
  언제든지 댓글 혹은 메일로 알려주시면 감사하겠습니다! 😸

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기