C++ R-Value를 이용한 이동 대입
- 프로그래밍/C/C++
- 2019. 1. 9.
C++ R-Value를 이용한 이동 대입
L-Value를 이용해서 밑에와 같이 간단한 프로그램을 만들었다고 치자.
main.cpp
#include "Resource.h"
#include "AutoPtr.h"
#include "Timer.h"
AutoPtr<Resource> generateResource()
{
AutoPtr<Resource> res(new Resource(10000000));
return res;
}
int main()
{
Timer timer;
{
AutoPtr main_res;
main_res = generateResource();
}
timer.elapsed();
}
AutoPtr.h
#pragma once
#include <iostream>
template<class T>
class AutoPtr
{
T *m_ptr;
public:
AutoPtr(T *ptr = nullptr) : m_ptr(ptr)
{
std::cout << "AutoPtr 기본 생성자" << std::endl;
}
~AutoPtr()
{
std::cout << "AutoPtr 소멸자" << std::endl;
if (m_ptr != nullptr) delete m_ptr;
}
AutoPtr(AutoPtr &a)
{
std::cout << "AutoPtr 복사 생성자" << std::endl;
m_ptr = new T;
*m_ptr = *a.m_ptr;
}
AutoPtr& operator=(const AutoPtr &a)
{
std::cout << "AutoPtr = 연산자 오버로딩" << std::endl;
if (&a == this)
return *this;
if(m_ptr != nullptr) delete m_ptr;
m_ptr = new T;
*m_ptr = *a.m_ptr;
return *this;
}
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool isNull() const { return m_ptr == nullptr; }
};
Resource.h
#pragma once
#include <iostream>
class Resource
{
public:
int *m_data = nullptr;
unsigned m_length = 0;
public:
Resource()
{
std::cout << "Resource 기본 생성자" << std::endl;
}
Resource(unsigned length)
{
std::cout << "Resource 길이 생성자" << std::endl;
m_data = new int[length];
m_length = length;
}
Resource(const Resource &res)
{
std::cout << "Resource 복사 생성자" << std::endl;
Resource(res.m_length);
for (unsigned i = 0; i < m_length; ++i)
m_data[i] = res.m_data[i];
}
~Resource()
{
std::cout << "Resource 소멸자" << std::endl;
if (m_data != nullptr) delete[] m_data;
}
Resource& operator=(Resource &res)
{
std::cout << "Resource = 연산자 오버로딩" << std::endl;
if (&res == this) return *this;
if (m_data != nullptr) delete[] m_data;
m_length = res.m_length;
m_data = new int[m_length];
for (unsigned i = 0; i < m_length; ++i)
m_data[i] = res.m_data[i];
return *this;
}
void print()
{
for (unsigned i = 0; i < m_length; i++)
std::cout << m_data[i] << " ";
std::cout << std::endl;
}
};
이 프로그램은 생성자가 언제 호출되고 소멸자가 언제 호출되고 연산자 오버로딩이 언제 호출되고 이런것을 알수있게 예제를 하나 작성하였다.
이 예제를 통해서 생성자가 언제 호출되고 사라지는지 미리 예측을 해보고 실행을 해보자.
디버깅 모드와 릴리즈 모드를 각각 실행해서 출력하는 내용과 시간을 보자.
디버깅 모드 |
릴리즈 모드 |
역시 릴리즈 모드는 엄청 빠르다.
그럼 이것을 조금 더 빠르게!! 더 빠르게 할 수가 있다.
어떻게 하냐면! R-Value를 이용해서 더 빠르게 해보자.
R-Value는 임시로 생성되는 메모리를 이용하는 것이다.
위의 코드를 보면 임시로 생성되는 메모리를 볼 수 있을것이다.
R-Value는 임시로 생성되는 메모리를 이용해서 더 빠르게 할 수 있다.
R-Value를 이용을 하기 위해서 AutoPtr.h 파일을 조금 수정을 했다.
#pragma once
#include <iostream>
template<class T>
class AutoPtr
{
T *m_ptr;
public:
AutoPtr(T *ptr = nullptr) : m_ptr(ptr)
{
std::cout << "AutoPtr 기본 생성자" << std::endl;
}
~AutoPtr()
{
std::cout << "AutoPtr 소멸자" << std::endl;
if (m_ptr != nullptr) delete m_ptr;
}
AutoPtr(AutoPtr &&a) : m_ptr(a.m_ptr)
{
a.m_ptr = nullptr;
std::cout << "AutoPtr 이동 생성자" << std::endl;
}
AutoPtr& operator=(AutoPtr &&a)
{
std::cout << "AutoPtr 이동 연산자 오버로딩" << std::endl;
if (&a == this)
return *this;
if (!m_ptr) delete m_ptr;
m_ptr = a.m_ptr;
a.m_ptr = nullptr;
return *this;
}
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool isNull() const { return m_ptr == nullptr; }
};
코드 상으로 보면 이전 AutoPtr과 다를게 거의 없다.
한번 실행을 해보자.
R-Value 디버깅 모드 |
R-Value 릴리즈 모드 |
보이는가? Resource 생성자는 한번만 호출이 되는것을?
그리고 속도도 엄청 빠르다.
R-Value를 이용하면 속도가 엄청 빠르게 되고 메모리도 절약?이 되는 효과를 볼 수 있다.