2023.05.16 - [C++] - [C++] LValue vs RValue. 총 정리
2023.05.10 - [C++/기타] - [C++] LValue vs RValue (2)
에서 이어지는 글.
value. glvalue와 rvalue의 특성을 모두 가지고 있다.
xvalue. [곧 사라지는 값] 이 가지고 있는 두 가지의 특성
1. 본인이 고유한 주소를 가지고 있다는 점.
2. 그 값을 이동 시킬 수 있다는 점.
에서 2 번째 특징을 예시 코드를 통해 정리해 보았다.
#include <iostream>
#include <vector>
using namespace std;
string getName(int i) {
if (i == 1) {
return string("someName");
} else {
return string("error");
}
}
int main() {
string name = getName(1);
cout << name << endl;
return 0;
}
상당히 자주 사용되는 함수 형태이다.
함수를 통해 원하는 값을 얻어 와 변수에 저장하는 형태. 이 때의 getName의 return 값은 xvalue 형식이다.
getName(1)이라는 함수의 값은 1번 특성으로 인하여 본인의 고유한 주소값을 가지고 있다는 것을 이미 알고 있다.
그렇다면 넘어왔을 때 이 값을 어떻게 사용하여 string name 이라는 변수에 넘겨 줄까?
#include <iostream>
#include <vector>
using namespace std;
class myClass {
public:
int mynum;
myClass() {
mynum = 0;
cout << "default constructor" << endl;
}
myClass(int a) {
mynum = a;
cout << "default constructor with " << a << endl;
}
myClass(const myClass& obj) {
mynum = obj.mynum;
cout << "copy constructor" << endl;
}
myClass& operator=(const myClass& obj) {
this->mynum = obj.mynum;
cout << "copy assign constructor" << endl;
return *this;
}
myClass(myClass&& obj) noexcept {
this->mynum = obj.mynum;
cout << "move constructor" << endl;
}
myClass& operator=(myClass&& obj) noexcept {
this->mynum = obj.mynum;
cout << "move assign constructor" << endl;
return *this;
}
~myClass() {
cout << "destructor " << mynum << endl;
}
};
해당 부분을 확인하기 위하여, 생성자가 호출 될 때 어떤 생성자가 호출되는지 알 수 있게 print 찍어주는 myClass를 하나 만들었다. 다만 각각의 생성자 내부에서 효율적으로 돌기 위하여 이동, 복사 등 본연의 역할을 다 해야 하지만, ( ex) 내부 변수가 primitive type이 아니라 vector/ string 등. 이동 시에 포인터 값을 옮겨주고 본인은 delete... ) 해당 부분은 패스하고 어떤 생성자가 호출된 상황인지 print만 찍어 주었다.
먼저 간단하게 위의 class를 사용하여 프로그램을 구현해 보았다.
int main() {
myClass m1class(4);
myClass m2class(5);
cout << "-----------------------------------" << endl;
m1class = m2class;
cout << m1class.mynum << endl;
return 0;
}
m1class, m2class가 기본 생성자로 만들어진다.
m2class = m1class를 할 때 m1class에 있던 값이 copy되어 m2class로 들어가게 되고, m2class 내부의 값은 5로 변경된다.
그리고 위 함수를 살짝 다르게 변경해 보았다.
myClass makemyClass() {
return myClass(5);
}
int main() {
myClass m1class(4);
cout << "-----------------------------------" << endl;
m1class = makemyClass();
cout << m1class.mynum << endl;
return 0;
}
위 코드와 비슷한 상황이지만, 직접 만들어서 넣지 않고 함수를 통하여 myclass를 얻어 온 후 assign 시켜 주었다. 이 때는 copy가 아닌 move가 호출되었다.
사실 m1class = m2class 에서 m2class는 const myClass& 에 묶여서 copy assign operator으로 가고,
m1class = makemyClass()에서 makemyClass()는 myClass&& 가 되어 각각에 맞는 move assigne operator에 들어간 거지만.. 여기선 참조를 빼 두고 설명해보자.
복사는 말 그대로 복사이다. 내가 가지고 있는 값과 같은 값을 상대방에게 알려주어서, 상대방이 그 값을 가질 수 있게 한다.
만약 본인이 한 값(ex) vector<string> vecstr)을 가지고 있고, 상대방에 복사를 해 갔다면 그 값은 메모리 상에 두 개가 존재하게 된다.
이동은 값을 상대방에게 주는 것이다. 내가 가지고 있는 값의 소유권을 넘기고, (보통) 넘겨준 object는 소멸해 버린다. 즉 해당 값은 메모리 상에 한 개만 존재하게 된다.
위의 코드 상황에서 m1Class = m2class 라는 코드를 수행 할 때, m2class는 자신의 값을 복사해 주어야 하지, 이동하면 본인의 값이 사라지기 때문에 이동해줄 수 없다. 가능은 해도, m2Class라는 값은 이동해 준 이후부터 사용이 불가능하다.
가능은 하다. m1class = std::move(m2class);
다만 이 방법이 모두에게 먹히는 것은 아니고, 먹힌다고 하더라도 그 값 자체가 moveable 하진 않고, 특수한 처리를 해 주어야 moveable 해 진다.
다만 m1class = makemyclass() 상황에서는 함수를 통해 만든 값이 어차피 곧 사라지는 값 (eXpring value)이기 때문에 이동을 해도 아무런 문제가 없다. 즉 그 값 자체로 이동 가능하다.
결국.
xvalue는 값을 이동 시킬 수 있다.
이렇게 정의를 내리고, 위 표를 다시 한 번 보니 1번에서 했던 정리가 뭔가 좀 어색해 보인다. glvalue와 rvalue에 대한 새로운 정의가 필요할 것 같다.
https://donot-simsim.tistory.com/58
사실, 1번에서 했던 정리는 약간 고전적인 방식의 lvalue, rvalue 분류라고 한다.
요즘에는, xvalue의 특징이었던
- 본인의 고유한 주소를 가지고 있다.
- 값이 moveable 하다.
이 두개를 가지고 다시 분류를 한다고 한다.
이 부분은 다음 포스팅에서..
2023.05.16 - [C++] - [C++] LValue vs RValue. 총 정리
*잘못된 정보는 알려주시면 감사한 마음으로 수정하겠습니다.
'C++ > 기타' 카테고리의 다른 글
[C++] type cast (static_cast, dynamic_cast, const_cast, reinterpret_cast) (0) | 2023.05.18 |
---|---|
[C++17] if constexpr (1) | 2023.05.17 |
[C++] LValue vs RValue (2) (1) | 2023.05.10 |
[C++] LValue vs RValue (1) (0) | 2023.04.29 |
[C++ 11] 정규 표현식 std::regex (2) (0) | 2023.04.23 |