0. 동적할당이란
- 메모리 할당을 동적으로 하는 것, 정적, 자동이 아닌
- 정적 메모리 할당은 컴파일 단계에서 정해진 값만큼만 할당할 수 있음
- 이 한계를 극복하기 위해 필요한 것이 동적 할당이며 메모리의 힙 영역에 할당되게 됨
- 정적 : 전역, static / 자동 : 지역, 매개변수 혹은 인자 / 동적 : malloc, new등 메모리 할당 함수를 통해
1. malloc, free
- C 스타일의 동적 할당 함수
- <stdlib.h>에 포함 된 함수
- void* malloc(size_t size);
int* pa = (int*)malloc(sizeof(int) * 10);
- malloc은 memory allocation의 약자
- 인자로 넘어가는 size는 byte 단위임, 위의 경우 int 4byte * 10 을 했으므로 40byte의 메모리를 할당
- 메모리가 할당된 시작 주소를 return 하여 int*로 cast하여 사용하는 것
- 할당된 40바이트에 대해선 pa 말고는 시작주소를 모르기 때문에 사용이 끝난 경우 pa가 보유한 40바이트의 시작주소를 통해 free를 해주어야 함
free(pa);
- 한 가지 의아한 점은 free의 경우 몇 byte를 해제할지를 인자로 넘겨주지 않아도 되는데 할당된 메모리의 시작 주소를 알면 몇 바이트를 할당했는지는 프로그램 수준에서 기억해주는 모양이다.
2. new, delete
- C++ 스타일의 동적 할당 함수
- 헤더 추가 없이 사용 가능
- cast 없어도 됨
int* pa = new int;
- malloc과 마찬가지로 할당한 메모리는 정리해야 함
delete pa;
- 배열로 할당
int * pa = new int[10];
- 배열 해제
delete[] pa;
3. malloc vs new
- 다른 차이는 다 제쳐두고 핵심이라 할 수 있는 차이는 new는 생성자를 호출한다는 점
#include <iostream>
class A
{
public:
A()
: a(1), b(2)
{}
A(int a, int b)
: a(a), b(b)
{}
public:
int a, b;
};
int main()
{
A* MyA1 = new A;
A* MyA2 = (A*)malloc(sizeof(A));
if (MyA2) //malloc은 메모리 할당 실패 시 nullptr을 반환
{
*MyA2 = A(); //malloc으로 할당한 메모리에 대해 생성자를 명시적으로 호출
}
if (MyA1)
{
std::cout << MyA1->a << std::endl; //1
}
if (MyA2)
{
std::cout << MyA2->a << std::endl; //1
}
return 0;
}
- 클래스를 굳이 malloc으로 초기화하고 싶다면 위와 같은 코드로 가능함
4. new로 배열 + 생성자 호출
A* MyA = new A[3]{A(1,2), A(3,4), A(5,6)};
- 위와 같이 [] 안에는 배열 크기, {} 안 에는 생성자를 직접 호출
- 위처럼 초기화를 하게 될 경우 복사나 이동 생성자가 불리는 게 아닌가 싶어서 테스트 코드를 작성해 봄
#include <iostream>
class A
{
public:
A()
: a(1), b(2)
{}
A(int a, int b)
: a(a), b(b)
{
std::cout << "인자 있는 생성자 호출" << std::endl;
}
A(const A& Rhs)
{
this->a = Rhs.a;
this->b = Rhs.b;
std::cout << "복사 생성자 호출" << std::endl;
}
A operator=(const A& Rhs)
{
this->a = Rhs.a;
this->b = Rhs.b;
std::cout << "복사 대입 연산자 호출" << std::endl;
}
A(A&& Rhs) noexcept
{
this->a = Rhs.a;
this->b = Rhs.b;
std::cout << "이동 생성자 호출" << std::endl;
}
A operator=(A&& Rhs) noexcept
{
this->a = Rhs.a;
this->b = Rhs.b;
std::cout << "이동 대입 연산자 호출" << std::endl;
}
public:
int a, b;
};
int main()
{
A* MyA = new A[3]{A(1,2), A(3,4), A(5,6)};
for (int i = 0; i < 3; i++)
{
std::cout << MyA[i].a << std::endl;
std::cout << MyA[i].b << std::endl;
}
return 0;
}
- 실행 결과, 복사나 이동 생성자는 불리지 않은 모습
5. virtualalloc, virtualfree
- malloc, new를 Commit, free, delete를 Free라고 했을 때 그 중간 단계인 Reserve가 있다는 것이 위 키워드의 핵심
- Reserve란 예약의 의미인데 왜 필요한가의 궁극적인 목표는 섬세한 메모리 조작이고, 이를 통해 메모리 단편화를 막고 성능 향상을 꾀하기 위함이다.
- 잘 정리된 블로그가 있어 링크를 남겨봄
'C++ > 키워드 정리' 카테고리의 다른 글
키워드 정리 [7] - 컴파일 (1) | 2023.12.01 |
---|---|
키워드 정리 [6] - 함수 호출 규약 (0) | 2023.12.01 |
키워드 정리 [4] - 구조체 (1) | 2023.12.01 |
키워드 정리 [3] - 매크로 (1) | 2023.12.01 |
키워드 정리 [2] - const (1) | 2023.12.01 |