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란 예약의 의미인데 왜 필요한가의 궁극적인 목표는 섬세한 메모리 조작이고, 이를 통해 메모리 단편화를 막고 성능 향상을 꾀하기 위함이다.
  • 잘 정리된 블로그가 있어 링크를 남겨봄

https://coding2021.tistory.com/4

'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

+ Recent posts