C++

오른값 참조

monstro 2024. 4. 22. 01:15
728x90
반응형

이번 포스팅에서는 오른값 참조에 대해서 알아보겠습니다.

오른값 참조는 비교적 최근인 C++11에 등장한 개념으로, 이로 인해 C++의 처리속도가 매우 빨라졌습니다.

 

그러나 오른값 참조에 대해 배우기 전에, 먼저 확실하게 알고 넘어가야 하는 부분이 있습니다.

바로, 왼값(l-value)과 오른값(r-value)이라는 개념입니다.

 

1) l-value VS r-value

우선, l-value는 단일식을 넘어서 계속되는 개체를 의미합니다.

그리고 r-value는 l-value가 아닌 나머지로 얘기할 수 있겠습니다.

 

예시를 들자면,

int a = 3;

이라는 코드가 있다고 해보겠습니다.

 

이때, a는 언제든지 값이 변할 수 있습니다.

가령, a = 10과 같은 형태로 값을 변하게 하는 것이 가능합니다. 따라서 a는 l-value에 속합니다.

반대로, 3은 값을 변하게 하는 것이 불가능합니다. 따라서 3은 r-value입니다.

 

왜 이렇게 분리해놓았을까요?

바로 필요에 따라 일시적으로 사용해야 하는 개체와 그렇지 않은 개체를 구분지어 놓는 것이 효율적이기 때문입니다.

 

그리고 l-value를 r-value로 만들어주기 위해서는 &&를 수식해주면 됩니다.

위의 오른값 참조 연산자를 통해 우리는 개체를 일시적인 개체로 만들어 줄 수 있습니다.

그렇다면 저런 변환은 어디에 사용하는 것이 좋을까요?

 

2) 얕은 복사와 깊은 복사의 한계

이전의 포스팅에서 우리는 얕은 복사와 깊은 복사의 한계를 명확히 보았습니다.

얕은 복사는 참조를 그대로 가져가 문제가 되었고, 깊은 복사는 높은 복사 비용이 문제가 되었습니다.

하지만, 오른값 참조를 이용하면 두 복사 방식의 이점을 챙기면서 단점은 극복하는 아주 효율적인 설계가 가능합니다.

 

 

위의 코드에서 k2는 복사 생성자를 통해 k1으로부터 복사되었습니다.

그리고 Knight 클래스에서 만든 생성자를 통해 _pet 포인터를 깊은 복사하게 되었습니다.

 

 

결과적으로 성공적인 복사가 이루어졌지만, pet포인터 이외에 다른 참조가 무수히 많다고 가정한다면

이 방식은 매우 비효율적이 될 것입니다. 

이런 경우에 우리는 오른값 참조를 사용할 수 있습니다.

 

3) 오른값 참조

 

Knight 클래스 안에 위와 같은 이동 생성자를 추가합니다.

서두에 말했듯이, 오른값 참조 &&는 l-value를 일시적인 개체로 만들수 있다고 하였습니다.

이를 통해, 기존의 _pet 포인터를 nullptr로 만들어 얕은 복사의 문제를 해결하였습니다.

 

또한, 깊은 복사의 문제였던 높은 복사 비용또한 해결하였습니다.

 

 

또한, 메모리  결과에서도 원하는 대로 작동한 것을 확인할 수 있습니다.

 

4) std::move()

사실, 위와 같이 작성하는 것은 매우 귀찮은 일입니다.

그러므로 우리는 move() 함수를 사용하여 위와 같은 과정을 단 한줄로 작성해줄 수 있습니다.

 

 

move() 함수는 위에서 볼 수 있는 일련의 코드를 단 한줄로 표현가능할 뿐더러 기능적으로도 거의 비슷힙니다.

 

 

또한 메모리 결과에서도 의도한 바로 잘 작동했음을 확인할 수 있습니다.

 

결론적으로, 이동은 오른값 참조를 통해 얕은 복사의 장점 + 깊은 복사의 장점을 이끌어 낼 수 있습니다. 

728x90
반응형

'C++' 카테고리의 다른 글

동적할당  (0) 2024.04.29
iterator(반복자)  (0) 2024.04.29
얕은 복사 VS 깊은 복사  (0) 2024.04.21
C++의 캐스팅 - const_cast  (0) 2024.04.15
C++의 캐스팅 - reinterpret_cast  (0) 2024.04.14