오른값 참조 등장 이후 모던 C++이 기존 C++보다 더 빠르게 작동하게 되었다.
1. 왼값(lvalue) vs 오른값(rvalue)
(1) 정의
- 왼값(Left Value) : 단일식을 넘어 계속 지속되는 개체, (C언어에서 ; 대입식에서 왼쪽에 오는 것)
int a = 3;
int b = 2; // 왼값 : a, b
- 오른값(Right Value) : 왼값이 아닌 나머지 (임시 값, 열거형, 람다, i++ ...), (C언어에서 ; 대입식에서 오른쪽에 오는 것)
int c = a * b; // 오른값 : a * b
(2) 왼쪽값 참조(Lvalue Reference)
class Knight
{
public:
void PrintInfo()
{
}
public:
int _hp = 100;
};
void TestKnight_Copy(Knight knight){}
void TestKnight_LValueRef(Knight& knight){}
void TestKnight_ConstLValueRef(const Knight& knight){}
int main()
{
Knight k1;
TestKnight_Copy(k1);
TestKnight_LValueRef(k1);
TestKnight_ConstLValueRef(k1);
}
임의의 클래스 knight를 만들고 이를 이용하는 여러 함수를 만들어본다.
기존의 왼값 참조인 TestKnight_LValueRef는 Knight 타입의 매개 변수를 받는다.
이 때 오른값의 직접적인 참조가 불가능하다.
TestKnight_LValueRef(k1);
TestKnight_LValueRef(Knight());
즉 두번째에서 Knight의 기본 생성자를 이용한 임시 객체를 참조할 수 없다.
하지만 const가 붙은 참조를 하면 임시 객체를 참조할 수 있다.
void TestKnight_ConstLValueRef(const Knight& knight){}
int main()
{ //~~
TestKnight_ConstLValueRef(k1);
}
단, const의 특성으로 인해서 값을 변경하는 것이 불가능(읽기 전용)하다.
또한 멤버 함수를 사용할 수도 없다(const가 붙으면 가능함).
(2) 오른값 참조(Rvalue Reference)
void TestKnight_RValueRef(Knight&& knight){}
TestKnight_RValueRef(Knight());
오른값 참조에서는 임시로 생성된 객체에 대한 접근이 가능해진다.
단, 오른값 참조로 선언하면 기존의 왼값 참조는 따로 구현해야한다.
TestKnight_RValueRef(k1); // 오류 발생
(3) 왼쪽값 참조 형 변환하기
오른값 참조에서는 임시 객체에 대한 접근만 허용하는 것 뿐만이 아니다.
TestKnight_RValueRef(static_cast<Knight&&>(k1));
이렇게 기존의 객체를 형 변환을 통해서 오른값 참조를 사용할 수 있다.
2. 오른값 참조의 특징
(1) 빠른 속도
class Pet
{
};
class Knight
{
public:
Knight () // 기본 생성자
{
}
Knight(const Knight& knight) // 복사 생성자
{
}
~Knight()
{
if (_pet)
delete _pet;
}
void operator=(const Knight& knight) // 복사 대입 연산자
{
// 깊은 복사
_hp = knight._hp;
if (knight._pet)
_pet = new Pet(*knight._pet);
}
public:
int _hp = 100;
Pet* _pet = nullptr;
};
위와 같이 Pet 클래스를 만들고 각 Knight 객체가 pet을 들게 한다.
이렇게 설정하기 위해서는 Pet 클래스의 pet 객체를 만들고 그것을 knight 객체가 들고있게 해야한다.
그렇게 되면 각 Knight의 객체들이 서로 다른 pet을 가지게 된다.
이런 코드는 추후에 코드가 방대해지고 여러 사양이 추가되면, Knight 객체 하나를 생성하는데 큰 리소스를 차지하게 된다.
이것을 방지하고 코드를 더 빠르게 작동하게 하기 위해 임시 객체를 수정하는 오른값 참조가 등장하게 되었다.
void operator=(Knight&& knight) // 이동 대입 연산자
{
// 얕은 복사
_hp = knight._hp;
_pet = knight._pet;
knight._pet = nullptr;
}
knight의 임시 객체를 만들어 그 임시 객체가 가지고 있는 pet의 포인터를 그대로 가져오게하는 방식으로 작동한다.
Knight k2;
k2._pet = new Pet();
k2._hp = 1000;
Knight k3;
k3 = static_cast<Knight&&>(k2);
k2는 임시 객체이고 k3는 k2의 정보를 그대로 이동시켜서 사용한다.
따라서 객체를 전체적으로 복사하는 것 보다 임시 객체의 정보를 이동시키는 것이 더 빠르게 작동할 수 있다.
(2) move 문법
참고로 static_cast 대신 move를 이용하여 캐스팅할 수 있다.
k3 = move(k2);
// 본래 이름 후보 중 하나가 rvalue_cast 였다고 한다..
'기초 C++ 스터디 > 모던 C++' 카테고리의 다른 글
10-9. 람다(lambda) 표현식 (0) | 2023.06.22 |
---|---|
10-8. 전달 참조 (forwarding reference) (0) | 2023.06.22 |
10-6. override, final (0) | 2023.06.20 |
10-5. delete - 삭제된 함수 (0) | 2023.06.20 |
10-4. enum class (0) | 2023.06.20 |