[C++] 이동 생성자 / 이동 연산자
카테고리: Cpp
태그: Constructor
이 글은 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를 반환하도록 바꾸자
참조
💻 열심히 공부해서 작성 중이니 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 알려주시면 감사하겠습니다! 😸
댓글 남기기