[C++] 우측값과 이동 시맨틱
Table of Contents
우측값이란? #
이름 | 설명 |
---|---|
좌측값 (l-value) |
단일 표현식 이후에도 없어지지 않고 지속되는 객체이다. 이름을 가지는 객체라고 볼 수 있다. 읽기, 쓰기가 가능하다. |
우측값 (r-value) |
표현식이 종료된 이후에는 더이상 존재하지 않는 임시적인 값이다. 읽기만 가능하다. |
// 왼쪽에 있는 것들은 모두 l-value이며,
// 오른쪽에 있는 것들은 모두 r-value이다.
int num = 3;
int* ptr = #
int sum = num + 3;
int res = sqrt(3);
++x; // 이것은 자기 자신을 리턴하므로 l-value이다.
x++; // 이것은 증가된 복사본을 리턴하므로 r-value이다.
우측값을 참조하는 방법 #
- 좌측값은
&
를 사용해서 참조한다. - 우측값은
&&
를 사용해서 참조한다.
// l-value 참조 방법
int num = 3;
int& lref = num;
lref = 4;
// r-value 참조 방법
int&& rref =3;
rref = 4; // 별명을 붙이니 l-value가 되어, 수정이 가능해졌다!
const
를 붙이면 우측값도&
로 참조할 수 있다.- 왜냐하면
const
를 붙이면 읽기만 가능해지기 때문이다.
- 왜냐하면
const int& ref = 3; // OK
이동 시맨틱(Move Semantics) #
- 비용이 비싼 깊은 복사를 하는 대신에 주소값만 가로채서 소유권을 이동시키는 것(얕은 복사)이다.
- 복사 생성자, 대입 연산자에 우측값 참조를 사용해서 이동 생성자, 이동 대입 연산자로 구현된다.
#include <iostream>
class MyClass
{
private:
int n; // 매개변수 수
char* pc; // 데이터를 가리키는 포인터
public:
MyClass(int num) : n(num)
{
cout << "일반 생성자" << endl;
pc = new char[n];
}
~MyClass()
{
cout << "소멸자" << endl;
delete[] pc;
}
MyClass(const MyClass& f) : n(f.n)
{
cout << "복사 생성자" << endl;
// 새로운 복사본이 만들어진다. (깊은 복사)
pc = new char[n];
for (int i = 0; i < n; i++)
pc[i] = f.pc[i];
}
MyClass(MyClass&& f) noexcept : n(f.n)
{
cout << "이동 생성자" << endl;
pc = f.pc; // 주소 가로채기
f.pc = nullptr; // 이전 객체가 아무것도 반환하지 않도록 함
}
MyClass& operator=(const MyClass& f)
{
cout << "복사 대입 연산자" << endl;
if (this == &f) return *this;
delete[] pc;
n = f.n;
// 새로운 복사본이 만들어진다. (깊은 복사)
pc = new char[n];
for (int i = 0; i < n; i++)
pc[i] = f.pc[i];
return *this;
}
MyClass& operator=(MyClass&& f) noexcept
{
cout << "이동 대입 연산자" << endl;
if (this == &f) return *this;
delete[] pc;
n = f.n;
pc = f.pc; // 주소 가로채기
f.pc = nullptr; // 이전 객체가 아무것도 반환하지 않도록 함
f.n = 0;
return *this;
}
MyClass operator+(const MyClass& f) const
{
MyClass temp = MyClass(n + f.n);
for (int i = 0; i < n; i++)
temp.pc[i] = pc[i];
for (int i = n; i < temp.n; i++)
temp.pc[i] = f.pc[i - n];
return temp;
}
};
int main()
{
MyClass one(1); // 일반 생성자 호출
MyClass two(one); // one은 l-value이므로 복사 생성자 호출
MyClass three = one + two; // one + two는 r-value이므로 이동 생성자 호출
}
- 좌측값도 이동 시맨틱을 하려면?
move()
를 사용하면 된다.
// move로 l-value가 r-value가 되어 이동 생성자 호출
MyClass four(std::move(one));