1. 이동 의미론: 기존 객체의 주소 및 값을 새로운 오브젝트에 소유권 이전하게 한다.
새로운 메모리 영역을 할당받는 것이 아닌 기존 메모리 영역을 새로운 변수의 메모리 주소로 이동한다.
깊은 복사를 할 필요 없이 효율적으로 이전할 수 있다.
각 객체, 클래스에 이동 생성자와 이동 대입 연산자가 정의되어 있어야 사용할 수 있다.
//&&은 보편 참조로, 문맥에 따라 r-value 참조가 될 수도, l-value 참조가 될 수도 있다
Test(Test&& other) //r-value reference
{
cout << "이동 생성자 호출" << endl;
mData = other.mData;
mName = other.mName;
other.mData = NULL;
other.mName = NULL;
}
2. 가질 수 있는 문제점
객체가 실제로 이동하는 게 아닌, 포인터를 이동하는 것이기 때문에 생기는 문제가 있다.
이전에 소유하고 있는 객체가 포인터를 들고 있으면 한쪽에서 delete를 할 때 dangling pointer 문제가 일어날 수 있다.
이런 사태를 방지하기 위해 이전에 소유하던 객체는 nullptr을 가지게 하는 편이다.
3. std::move
실제 이동을 하는 함수가 아니고 l-value를 r-value로 바꿔주는 역할을 한다.
이동 연산자가 정의되어 있을 경우 변경한 r-value로 오버로딩 할 수 있다.
이동 준비가 목적이다.
forward보다 사용이 편하고 코드의 명확성이 좋아진다.
std::vector<std::string> v;
std::string str = "example";
v.push_back(std::move(str)); // str 은 이제 껍데기만 남음
str.back(); // 정의되지 않은 작업!
str.clear(); // 다만 clear 자체는 가능하다.
4. std::forward
오른값일 때는 오른값으로 전달, 왼값일 때는 왼값으로 전달하는 역할을 한다.
보편(universal) 참조일 때 사용한다.
C++은 특정 문법에서는 참조에 대한 참조가 허용되는데, 이것이 템플릿 인스턴스화다.
또한, C++에서는 연역 과정에서 둘 중 하나라도 l-value 참조라면 결과는 l-value 참조가 된다는 규칙이 있다.
전달이 목적이다.
// lvalue를 캐치하는 함수
void Catch(Person& p, const char* name)
{
cout << name << "lvalue catch" << endl;
}
// rvalue를 캐치하는 함수
void Catch(Person&& p, const char * name)
{
cout << name << "rvalue catch" << endl;
}
// 전달받은 obj를 std::forward를 통해서 catch 함수로 전달합니다.
template<typename T>
void ForwardingObj(T&& obj, const char* name)
{
Catch(std::forward<T>(obj), name);
}
int _tmain(int argc, _TCHAR* argv[])
{
Person p1("ahn", 1985);
ForwardingObj(p1, "p1\t\t=\t");
ForwardingObj(std::move(p1), "std::move(p1)\t=\t");
return 0;
}
참고자료
https://chogyujin-study.tistory.com/42
https://velog.io/@15ywt/c-rvalue-reference
https://jungwoong.tistory.com/53
'CS 지식' 카테고리의 다른 글
[C++] Name Mangling (Decoration) (0) | 2025.04.14 |
---|---|
[C++] 스마트 포인터 (0) | 2025.04.13 |
[C++] 포인터 (0) | 2025.04.13 |
[C++] L-value, R-value (0) | 2025.04.13 |
객체지향 설계의 5가지 원칙 (SOLID) (0) | 2025.04.11 |