copy /move assignment

김대익·2022년 3월 4일

copy assignment

object1과 object2가 있고
object2를 object1에 assignment한다고 하면
object2의 내용이 object1에 전부 복사된다.

Cat & operator=(const Cat& other) {
	mName = other.mName;
    mAge = other.mAge;
    cout << "copy assignment << endl; 
    return *this
}

copy assignment의 return 타입은 reference이다.
그러므로 이 object의 주소가 가리키는 것을 리턴해주면 된다(return *this)

int main() {
	Cat kitty{"kitty", 1};
    Cat nabi{"nabi", 2};
    kitty = nabi;
    kitty.print();
}

를 출력해보면 nabi object의 정보가 모두 kitty에 복사되어
nabi 2가 출력된다.


2개의 object가 있을 때 object2가 R value라면
object2의 내용이 copy되는 것이 아니라 move된다

Cat & operator=(const Cat&& other) {
	mName = std::move(other.mName);
    mAge = other.mAge;
    cout << "move assignment << endl; 
    return *this
}

string object인 other.mName은 R value로 바꾸고 ownership을 뺏어올 수 있다.
int 타입인 mAge는 복사를 해주었다.

move assignment를 call해주기 위해

int main() {
	Cat kitty{"kitty", 1};
    Cat nabi{"nabi", 2};
    kitty = std::move(nabi);
    kitty.print();
    nabi.print();
}

kitty = std::move(nabi);를 사용하였다


출력해보면 kitty object에게 string의 ownership을 빼앗겨 nabi없이 2만 출력되는 것을 볼 수 있다.


self assignment

int main() {
	Cat kitty{"kitty", 1};
    Cat nabi{"nabi", 2};
    
    kitty = kitty;
    kitty = std::move(kitty);
}

이 상태로 빌드하면 문제없이 빌드가 된다

그러나 char * mPtr처럼 char pointer를 이용해 리소스 관리를 해주었다면
예상치 못한 일들이 생길수 있다
이를 방지하기 위해 두 개가 같은 object라면 자기자신을 return해주는 코드를 넣는다

Cat & operator = (Cat && other) {
  if (&other == this) {
      return * this;
  }
  ...
  ...
}

kitty = nabi; // copy assignment
kitty = std::move(nabi); // move assignment
가 일어났을때를 그림으로 보면

copy assignment는 kitty의 내용이 nabi의 내용으로 copy되는 것을 알 수 있고

move assignment는 nabi에서 가리키던 것이 kitty가 가리키도록 ownership이 변경된다는 것을 알 수 있다.


destructor, move assignment, move constructor는 exception이 던져지지 않기 때문에 noexcept를 붙여주는 것이 중요하다.


앞에서 말했듯 char * mPtr처럼 포인터를 이용해 리소스를 관리하지 않는다면
Cat(), ~Cat(), Cat(Cat& other)등 이렇게 직접 만들 필요는 없다.

int main() {
	Cat kitty{"kitty", 1};
    Cat nabi{"nabi", 2};
    
    kitty = kitty;
    kitty = std::move(kitty);
}

이렇게 포인터를 이용하지 않는 상태에서 copy, move constructor를 정의하지않고 빌드해도
컴파일러가 알아서 만들어주기 때문에 괜찮다.

따라서 생산성 높은 c++개발을 하고싶다면 포인터를 이용한 리소스관리는 피해주는 것이 좋다.


constructor, copy constructor, copy assignment를 막는 방법은
Cat() = delete;
Cat(const Cat& other) = delete;
Cat& operator = (const Cat& other) = delete;
처럼 = delete를 붙여주면 된다.

C++ 11이전에는

이렇게 private안에 넣어두는데 이는 = delete를 붙이는 것과 똑같이 동작한다.


static method만 가지고 있는 class의 경우 object를 만들 필요가 없다.
그럴 때는 Cat() = delete;를 이용해 constructor자체를 막는 것도 좋은 방법이다.

singleton pattern의 경우 하나만 있어야하므로 copy constructor, copy assignment자체가 존재하면 안되므로 막는 것이 좋다.

0개의 댓글