<개인적으로 공부한 내용들로, 내용이 완벽하지 않을 수 있습니다. 부족한 부분/틀린 부분에 대한 Comment 환영합니다.>
C++11에서는 Move semantics라는 개념이 도입되었습니다.
move semantics가 도입된 배경에 대해 이해하기 위해 아래 예제를 보도록 하겠습니다.
### c++
#include <iostream>
using namespace std;
class Number
{
public:
explicit Number(int i)
: m_number(new int(i))
{
cout << "Number Constructor is called(" << i << ")" << endl;
}
~Number()
{
if (m_number)
cout << "Number Destructor is called(" << *m_number << ")" << endl;
delete m_number;
}
Number(const Number& rhs)
: m_number(new int(*rhs.m_number))
{
cout << "Number copy constructor is called" << endl;
}
static void printValue(Number n) {
cout << "print " << *n.m_number << endl;
}
private:
int* m_number;
};
int main()
{
Number one(1);
Number::printValue(one);
}
설명을 위해 의도적으로 m_number를 heap에 할당했습니다.
위 예제를 실행하면 아래와 같은 결과가 나옵니다.
Number Constructor is called(1)
Number copy constructor is called
print 1Number Destructor is called(1)
Number Destructor is called(1)
printValue의 인자로 넘겨주는 과정에서 한번의 복사과정이 발생하게 됩니다. 불필요한 객체를 막기 위해 printValue를 아래와 같이 바꿀 수 있습니다.
### c++
static void printValue(const Number& n) {
cout << "print " << *n.m_number << endl;}
핵심은 reference로 변경한 것입니다. (reference는 원본을 참조하므로 내부에서 수정을 못하도록 const를 붙여주어야 합니다.)
이제 복사 생성자(copy constructor)가 호출되지 않을 것입니다.
이제 예제를 좀 더 확장해 아래와 같은 경우를 보도록 하겠습니다.
### c++
// in main
Number x(one); // (1)
이 경우 x와 one은 별도의 객체이므로 복사 생성자가 불려야 합니다.
### c++
class Number {
...
Number operator+(Number& rhs)
{
return Number(m_number + rhs.m_number);
}...
};
Number make_number()
{
return Number(2);
}
//in main
Number value(make_number()); // (2)
Number sum(one + two); // (3)
value와 sum을 만들기 위해서는 모두 두번의 복사 생성자가 불립니다.
이 경우에 불리는 복사 생성자는 사실상 불필요합니다. 하지만 제거할 방법은 마땅하지 않습니다.
물론 일부 compiler에서 최적화를 지원합니다. (Return Value Optimization : RVO)
gcc 의 경우 -fno-elide-constructors 를 사용하면 이 기능을 끌 수 있습니다.
현재 예제의 경우는 간단하기 때문에 임시로 생성되는 객체들에 대한 부담이 적을 수 있습니다만, Number가 아니라 Vector와 같은 많은 리소스를 사용해야 하는 경우라면 이것은 그대로 비효율이 됩니다.
(1)과 (2), (3)에는 한가지 중요한 차이점이 있습니다.
(1)은 lvalue를 받은 경우이고, (2), (3)은 rvalue를 넘겨받은 경우라는 사실입니다.
c++11부터는 이동 생성자가 생겼습니다.
rvalue reference를 인자로 받는 새로운 생성자입니다.
위 예제에서 아래와 같이 이동 생성자를 추가하겠습니다.
### c++
Number(Number&& rhs)
{
cout << "Number move constructor is called" << endl;
m_number = rhs.m_number;
rhs.m_number = nullptr;
}
이동 생성자가 복사 생성자와 다른 점은 rvalue reference로 받아서 받은 rhs의 m_number를 그대로 사용한다는 점입니다.
따라서, 복사 생성자가 불렸을 때와 비교해 불필요한 메모리 할당/해제가 사라지게 됩니다.
참조 : http://stackoverflow.com/questions/3106110/what-is-move-semantics
'프로그래밍 언어 > C&C++' 카테고리의 다른 글
GCC options (0) | 2013.12.05 |
---|---|
[C++11] Range based for loop (4) | 2013.11.13 |
[c++11] rvalue reference (1) | 2013.11.07 |
[C++11] Variadic template (0) | 2013.11.06 |
[C++11] unique_ptr (7) | 2013.11.01 |