개인의 학습을 목적으로 정리한 글입니다. 이점 참고하고 읽어주세요 ;)
#pragma once
#include <iostream>
template<class T>
class AutoPtr
{
public:
T* m_ptr;
public:
AutoPtr(T* ptr = nullptr)
:m_ptr(ptr)
{
std::cout << "AutoPtr default constructor" << std::endl;
}
~AutoPtr()
{
std::cout << "AutoPtr desturctor" << std::endl;
if (m_ptr != nullptr) delete m_ptr;
}
AutoPtr(const AutoPtr& a) // L-value reference
{}
AutoPtr& operator =(const AutoPtr& a)
{}
AutoPtr(AutoPtr&& a) // R-value
: m_ptr(a.m_ptr)
{}
AutoPtr& operator =(AutoPtr&& a) // 지울 것이기 때문에!
{}
};
AutoPtr 클래스의 전체 코드를 올리기엔 가독성도 떨어질 것 같아서 전체적인 틀만 보여드렸습니다.
훑어보시면 AutoPtr클래스에서 L-value를 사용하는 복사 생성자와 대입 연산자와 함께
R-value를 사용하는 복사 생성자와 대입 연산자도 멤버로 가지고 있는 것을 알 수 있습니다.
#include "AutoPtr.h"
#include "Resource.h"
using namespace std;
int main()
{
{
AutoPtr<Resource> res1(new Resource(10000000));
cout << res1.m_ptr << endl;
AutoPtr<Resource> res2 = res1;
cout << res1.m_ptr << endl;
cout << res2.m_ptr << endl;
}
}
AutoPtr.h 헤더 파일과 Resource.h 헤더 파일을 include 하여 사용하고 있는 main함수를 보시면,
AutoPtr 자료형으로 res1을 선언한 다음,
똑같이 AutoPtr 자료형인 res2를 선언함과 동시에 res1을 대입하고 있습니다.
이 경우에는 AutoPtr클래스에서 L-value를 사용하는 복사 생성자를 호출하게 되어 그 과정에서 AutoPtr과 Resource를 한 번씩 더 호출하게 됩니다. 하지만
AutoPtr<Resource> res2 = std::move(res1);
res2에 res1로 초기화할 때, std::move()로 res1을 묶게 되면,
res1을 L-value가 아닌 R-value로 여기게 되고 AutoPtr클래스에서 역시 R-value를 매개변수로 받는 복사 생성자를 호출합니다!
=을 사용하는데 왜 대입 연산자가 아니라 복사 생성자인가요?라고 혹시 생각하신다면,
res2의 초기화가 완료된 상태에서 res1을 대입하는 것이 아니라,
res2를 초기화하는 과정이기 때문에 대입 연산자가 아니라 복사 생성자가 호출됩니다.
그래도 헷갈리신다면
AutoPtr<Resource> res2(std::move(res1));
이 코드가 위에 있는 코드와 동일하다는 걸 생각해주세요! 실행 결과 역시 동일합니다.
그리고 출력 결과를 보면
R-value를 사용하는 move-semantics에 의해서, res2가 res1의 주소를 그대로 이어받는 걸 알 수 있습니다.
주의하실 점은
res2가 res1의 포인터 주소를 넘겨받으면서, res1에는 어떤 주소도 남아있지 않습니다.
때문에 프로그램에서 위의 예제에 나오는 res1과 같이 앞으로 더 이상 사용하지 않는 객체 혹은 변수일 경우에만 R-value로 사용해야 합니다.
그렇지 않고 프로그램상 나중에 또 사용해야 하는 데 R-value로 사용한다면, 이미 그 값은 사라져 버린 뒤라 문제가 발생하게 됩니다.
'Information Technology > C++' 카테고리의 다른 글
[C++] 입출력 연산자 오버로딩 (0) | 2019.11.15 |
---|---|
[C++] 산술 연산자 오버로딩 (0) | 2019.11.15 |
[C++] std::move(2) (0) | 2019.11.15 |
[C++] 이동 생성자와 이동 대입 (0) | 2019.11.15 |
[C++] 깊은 복사, 얕은 복사, 그리고 대입 연산자 (0) | 2019.11.13 |