Channi Studies

[C++] 대입 연산자 오버로딩 (이동) | Assignment Operator Overloading (Move) 본문

C++/기타

[C++] 대입 연산자 오버로딩 (이동) | Assignment Operator Overloading (Move)

Chan Lee 2024. 1. 9. 15:34

저번 포스트에서 다룬 복사 방식의 대입 연산자 오버로딩은 l-value에 대해서 작동합니다. 

 

이번 포스트에서 다룰 이동 대입 연산자 오버로딩은 r-value를 다룹니다.

 

다음 코드 조각을 살펴봅시다.

Mystring s1;
s1 = Mystring{"Ricky"};	// Move Assignment

 

여기서 empty string을 담는 Mystring 객체 s1을 선언했습니다.

그리고 s1에 Mystring{"Ricky"}를 통해서 Ricky라는 문자열을 속성으로 지닌 Mystring 객체를 할당했습니다.

 

여기서 중요한 점은, s1에 값을 저장하기 위해서 

새로운 이름 없는 임시 객체를 만들고, 그 값을 s1에 저장을 한 뒤에 임시 객체를 제거합니다.

이것이 r-value 객체입니다.

 

하지만 아시다시피 이런 임시 객체의 생성은 메모리적 측면에서 매우 비효율적입니다.

이러한 이유로 우리는 move assignment operator를 구현합니다.

 

 

기본적인 syntax는 다음과 같습니다.

(이 포스트에서 예시로 사용하는 Mystring 클래스는 저번 포스트에 자세히 나와있습니다.)

 

[C++] 대입 연산자 오버로딩 (복사) | Assignment Operator Overloading (copy)

C++에서는, 한 객체에서 다른 객체로의 대입 연산자 사용이 가능합니다. Mystring s1 {"Frank"}; Mystring s2 = s1;// NOT Assingment // Same as Mystring s2 {s1}; s2 = s1// Assignment C++ 컴파일러는 기본 대입 연산자를 생성

code-studies.tistory.com

// syntax
Type &Type::operator=(Type &&rhs);

// example
Mystring &Mystring::operator=(Mystring &&rhs);

s1 = Mystring("Joe");	// move operator= called
s1 = "Ricky";		// move operator= called

 

Move assignment operator의 코드 정의를 살펴볼까요?

 

Mystring &Mystring::operator=(Mystring &&rhs){
    if (this == &rhs)
        return *this;
        
    delete [] this.str;
    str = rhs.str;
    
    rhs.str = nullptr;
    
    return *this;
}

copy operator과 매우 유사하지만,

deep copy방식 대신에 rhs의 임시 객체가 가르키는 포인터를 가져온다는 차이가 있습니다.

 

스스로의 포인터를 할당 해제한 뒤,

str = rhs.str 으로 포인터를 가져옵니다.

 

이후, 가져온 소스인 rhs의 포인터를 nullptr로 해제한 뒤,

*this를 통해서 현재 객체를 반환합니다.

 

 

만약 여기서 각 생성자 혹은 대입 연산자의 호출 시 출력하도록 하게 하고,

다음과 같은 메인 함수를 실행한다면 이런 결과가 나옵니다.

int main() {
  Mystring s1;
  s1 = Mystring("Ricky");	// move assignment

  Mystring s2{s1};		// copy constructor

  Mystring s3;			// (no-args constructor)
  s3 = s2;			// copy assignment

  return 0;
}

 

유의해야 하는 점은 Mystring s2{s1}과 Mystring s2 = s1;은 같은 것인데요,

=가 있기 때문에 assignment operator가 실행될거라고 착각할 수 있습니다.

 

하지만 그것은 아니고 객체의 생성에서의 = 연산자는 우리가 기존에 사용하던 대입 연산자의 형태로 실행됩니다.

 

우리가 오버로딩한 대입 연산자의 활용은 특정 객체의 생성 이후,

해당 객체에 대하여 대입 연산을 실행 했을때 실행됩니다.