헤더파일
이동 본문
sizeof(vector<Model>)
32비트 - 디버깅 모드 일땐 16바이트, 디버깅을 위한 정보가 필요해서
- 릴리즈 일땐 12바이트
64비트 - 디버깅 - 24비트
릴리즈 - 32비트
vector가 3개를 받기로 했다면 힙에 3개를 설정하고 메모리를 차지한다.
앞뒤 공간은 내꺼가 아니기 때문에 10개를 다시 잡는다면 다이나믹하게 공간을 새로 잡아 이사가고 전에꺼를 지운다.
벡터는 size, capacity, 힙가리키는 pointer로 구성.
class Model
{
public:
int size{0};
char* data=nullptr;
int id;
public:
Model() :id{++gid} { cout<<id << " - 디폴트 생성\n"; }
Model(const int& num) :size{num}, id{ ++gid }
{
data = new char[num];
cout<<id << " - (int)생성\n";
}
~Model()
{
cout <<id<< "- 소멸자: " << size << "바이트: " << static_cast<void*>(data) << endl;
if (data != nullptr)
{
delete[] data;
data = nullptr;
}
}
Model(const Model& other) :size{other.size}, id{ ++gid }
{
data = new char[size];
memcpy(data, other.data, sizeof(other.data));
cout<<id << " - 복사 생성\n";
}
Model& operator=(const Model& other)
{
if (this == &other)
return *this;
if (data != nullptr)
delete[] data;
size = other.size;
data = new char[size];
memcpy(data, other.data, sizeof(other.data));
cout << "= 복사\n";
return *this;
}
};
vector<Model> v;
Model a(333);
v.push_back(a);
지역변수 v와 a의 번지를 알아보자.
스택에 있는 v의 번지 : 0075FC4C
스택에 있는 a의 번지 : 0075FC58
딱 12차이 나게 이어져서 스택에 할당된다.
push_back에 넣은 변수는 레퍼런스거나 이동으로 전달되기 때문에 복사될 일은 없다.
Model a(333);
Model b(7777);
v.push_back(a);
v.push_back(b);
1 - (int)생성
2 - (int)생성
3 - 복사 생성
4 - 복사 생성
5 - 복사 생성
3- 소멸자: 333바이트: 00DEFCD0
2- 소멸자: 7777바이트: 00DEDE60
1- 소멸자: 333바이트: 00DEDD08
5- 소멸자: 333바이트: 00DF1C98
4- 소멸자: 7777바이트: 00DEFE28
v, a, b 는 모두 스택에 기본 크기만큼 할당된다.
a, b는 힙에 넘겨받은 숫자만큼 메모리를 잡는다.
push_back(a)가 실행됬을 때 Model하나가 들어갈 만큼의 메모리를 vector가 잡고 a를 복사생성해서 가져온다.
push_back(b) 가 실행됬을 때 Model두개가 들어갈 만큼의 메모리를 vector가 잡고 a와 b를 복사생성해서 가져온다. 전에 만든 a의 복사생성한 객체는 안 쓰임으로 버린다.
이런식으로 하면 수많은 사용하지 않는 객체가 만들어 지고 없어질 것이다.
그래서 이동을 통해 이문제를 해결할 수 있다.
벡터는 자신이 관리하는 배열을 HEAP을 만든다.
벡터는 배열에 들어갈 원소가 이동을 지원하는 클래스라면 자동으로 이동을 이용한다.
프로그래머는 자신이 만든 클래스에 이동생성자와 이동할당연산자를 프로그램하면 된다.
클래스를 프로그램하는 프로그래머는
생성자, 소멸자, 복사생성자, 복사할당연산자 이동생성자, 이동할당연산자 중 하나를 프로그램하였다면 나머지도 반드시 프로그램해야한다. - 굉장히 강조된다.
이동은 옵션이지만 쓰면 훨씬 효율적이다.
자원은 파일, 메모리, 소켓, 락이 있는데 자원은 내꺼가 아니기 때문에 쓰고 반납해야 한다.
Model(Model&& other) noexcept :size{other.size}, id{ ++gid }
{
data = other.data;
//이동의 대상이 되는 클래스는 well-known상태로 만들어 두자.
other.size = 0;
other.data = nullptr;
cout << id << " - 이동 생성" << size << " 바이트: "<< static_cast<void*>(data) << endl;
}
noexcept : C++14에서 이동 생성자는 예외를 던지면 안된다.
이렇게 이동생성자를 만들면 아까처럼 333의 임시객체는 버리지만 안에 힙에 메모리는 포인터로 가져온다.
메모리를 깊은 복사로 잡아온다.
#include<type_traits>
template<typename T>
T f(T a, T b)
{
static_assert(is_arithmetic<T>::value, "산술연산 안되는 타입이야");
return a + b;
}
is_arithmetic으로 현재 자료형이 산술연산이 되는지 확인할 수 있다.