예제 1
이를 그림과 함께 설명해보면,
1) 스택에 a가, 힙에 "Headless" 가 존재한다.
2) 스택에 string object b 가 올라가면서 move()를 통해서 b가 "Headless"를 가리키게 된다. (a의 소유권이 이전됨)
3) b를 출력하게 되면 "Healess"가 출력되고, 이후 a를 출력하게 되면 a는 아무 것도 가리키지 않기 때문에 출력값이 비어있게 된다.
즉, move() 키워드는 Lvalue를 Rvalue로 바꾸면서 resource ownership 을 다른 object에게 넘겨줄 수 있다는 것이다.
이 예제에서는 a가 가지고 있던 힙 공간에 "Headless"의 ownership을 move() 명령어를 통해서 b에 넘겨준 것이다.
예제 2
함수를 콜 했을 때 레퍼런스를 넘겨주었다는 것 = 현재 string a 를 함수 안에서 마음대로 바꿀 수 있다.
따라서 함수 안에서 s 를 "Headless"로 재정의 해주게 되면, storeByLRef(a)를 콜 하는 순간 "abc" 가 쓰여있던 a 가 "Headless" 으로 재정의되며 출력이 "Headless"로 바뀌는 것을 확인할 수 있다.
예제 3
local object인 b에 a가 넘겨준 것을 복사copy 없이 바로 저장할 수 있는 방법
std::move(s) 를 사용하면 문자열 "abc"는 단 한번의 복사도 없이 지역변수 b에 저장될 수 있다. 이 때 a는 "abc"에 대한 소유권을 빼앗기면서 더 이상 "abc"를 소유하지 않게 된다.
실행해 보면 string a 에는 더 이상 "abc"가 들어 있지 않은 것을 확인할 수 있다.
string a 가 함수 안에서 바뀌지 않기 위해 const 를 붙여줄 수 있다.
실행해 보면 a에 abc가 제대로 나오는 것을 확인할 수 있다.
이 때 로컬 변수 b에도 abc가 들어 있는 것을 확인할 수 있다.
즉 const 를 붙여준채로 std::move()를 실행시키게 되면 copy가 일어나게 된다.
예제 4
이 때, Cat의 멤버변수 mName에 값을 넘겨줄 때, 가장 효율적인 방법은?
1) setName(s) 즉 Lvalue s를 넘겨주게 되면 복사가 2번 일어나게 되서 효율적이지 못하다.
2) setName에 레퍼런스를 받고 std::move()를 사용하게 되는 경우,
Lvalue를 넘겨주게 되면 1copy. 하지만 이 경우 kitty 문자열에 대한 소유권이 mName에 넘어가게 되면서 s에는 아무것도 남지 않게 됨.
-이를 방지하기위해 const를 넣어줄 수 있는데, 이렇게 되면 정말 필요한 1copy가 일어난다.
다만, 이 경우 Rvalue를 넘겨주었을 때 1번의 copy가 일어나게 되니 효율적이지 못하다.
결국 Lvalue를 넘겨주었을 때는 1번의 copy, Rvalue를 넘겨주었을 때는 0copy가 목적이다.
이 방법은, 함수 argument를 값으로 받고 그 안에서 std::move()를 통해서 멤버변수 mName을 세팅해 주게 되면 Lvalue가 넘어왔을 때는 1번의 복사만, Rvalue가 넘어왔을 때는 0 번의 복사가 일어나게 된다.
kitty.setName(s) 까지의 프로세스
1) 스택에 string object mName은 비어있다.
2) string s 를 정의하며 "kitty" 문자열을 넣어주었다. (힙 공간 어딘가에)
3) 멤버함수 setNmae() 실행.
: string object를 값으로 받음. setName 함수가 실행되며 스택 프레임이 올라오고, 이 안에는 name이 있고
값으로 받았기 때문에 "Kkitty"는 복사가 된다. argument name은 "kitty"를 가리키고 있다.
4) name의 소유권이 std::move()를 통해, kitty의 소유권이 멤버 객체 mName으로 넘겨감/
5) setName 함수가 끝나면서 스택 프레임이 해제됨.
*Lvalue가 이동하며 단 1번의 복사만 일어나는 것 확인 가능
kitty.setName("nabi") 프로세스
1) 스택에 kitty.mName string 공간이 생기고 이 공간은 어딘가를 가리키고 있는데, 이 공간에는 아무것도 쓰여있지 않다.
2) kitty.setName("nabi") 함수 실행.
: 순수하게 value로 받는 name에 Rvalue "nabi"가 들어오게 된다.
** copy elision (optimization)
: setName 함수가 실행 되는 순간 name이 스택에 올라가게 되고 힙 공간을 가리키게 됨.
컴파일러는 이미 넘어오는 값이 Rvalue임을 알고 있기 때문에, 복사하지 않고
name이 가리키는 곳에 바로 문자열"nabi"를 적어준다.
3) move 키워드를 통해 문자열 "nabi"의 소유권이 kitty.mName으로 넘어가게 된다.
*copy elision 룰에 따라 0복사를 만들어 냄.
'모던C++ > Resource move' 카테고리의 다른 글
4. RVO - Return Value Optimization (0) | 2022.07.23 |
---|---|
2. L-value, R-value - 0copy 만들기 (0) | 2022.07.21 |
1. 포인터와 레퍼런스, Pass by Value (0) | 2022.07.17 |