malloc는 C스타일의 동적 할당이고, new는 C++ 스타일의 동적 할당이다.
둘의 차이를 알아보도록 하자.
1. 반환형의 차이
malloc의 경우, 말 그대로 메모리만 할당을 해준다. 내가 20바이트를 요구했다면 정확히 20바이트의 메모리 공간을 할당하여, 가장 앞의 주소값을 반환해준다. 메모리 공간만 할당해주었기 때문에 반환형이 void* 이다.
그렇기 떄문에 malloc으로 할당받은 메모리 영역을 사용하기 위해선 원하는 자료형으로 타입캐스팅을 해야 한다.
반면, new의 경우는 내가 요청한 자료형으로 타입캐스팅을 해서 반환해준다. int형으로 요청했다면 int*타입으로 반환해주며, Test라는 클래스로 요청했다면 Test*형으로 반환해준다.
2. 초기화의 차이
malloc의 경우 해당 메모리 영역을 초기화하지 않고, 그대로 넘겨준다. 개발자쪽에서 직접 초기화를 해주어야 한다.
반면, new의 경우 기본 생성자를 호출하여 메모리를 초기화 한 뒤에 반환해준다.
물론, 아래 코드처럼 인자를 넣어 오버로딩한 생성자를 사용할 수도 있다.
class Test
{
public:
Test(){}
Test(int _X) : X(_X){}
int X = 0;
};
int main
{
Test* T1 = new Test();
Test* T2 = new Test(3);
}
3. 메모리 크기 조절 여부
malloc의 경우 realloc이라는 함수를 사용하여 할당받은 메모리영역의 크기를 조절할 수 있다.
물론 안정성, 오버헤드 등의 이유로 권장되지는 않는다고 한다. 하지만, 크기를 조절하는 것이 가능은 하다.
반면, new의 경우는 재조정을 할 수가 없다. 크기를 늘리고 싶다면 새로운 메모리를 할당받은 뒤, 기존 데이터를 복사하고 다시 기존의 메모리 영역을 해제하는 작업을 해야한다.
4. 예외처리 방식
malloc의 경우 메모리 할당을 실패하게 되면, NULL을 반환하도록 되어있다.
그렇기 때문에 malloc으로 할당받은 메모리 영역에 대해서는 아래와 같이 예외처리하는 것이 일반적이다.
int main()
{
int* I = (int*)malloc(sizeof(int));
if(I == NULL)
{
std::cout << "Memory allocationg failed";
return 1;
}
return 0;
}
다만, new의 경우는 다르다. 메모리 할당이 실패하면 null을 반환하지 않고 bad_alloc이라는 예외가 발생한다.
그렇기 떄문에, 이 예외에 대한 처리를 해주지 않으면 프로세스가 그대로 종료되어 버린다.
try
{
int *I = new int();
}
catch (std::bad_alloc &exception)
{
cout << "exception :" << exception.what() << endl;
std::abort();
}
하지만, 이렇게 사용하면 new를 할 때마다 try-catch를 해주어야 하고 매우 번거롭고 코드도 지저분해진다.
그래서, 아래와 같은 방식을 사용하는 것이 권장된다.
void handler()
{
cout << "Memory allocating is failed" << endl;
std::abort();
}
int main(void)
{
std::set_new_handler(handler);
int *arr = new int();
return 0;
}
std::set_new_handler()는 c++ 표준 라이브러리에서 제공해주는 함수이다.
함수를 콜백으로 등록해놓으면, new가 실패할 때 알아서 등록한 함수를 기반으로 예외처리를 실행해준다.
5. 메모리 해제 방법의 차이
malloc으로 할당받은 메모리 영역은 free로 해제해준다.
반면, new로 할당받은 메모리 영역은 delete로 해제해주어야 한다.
여기서 한 가지 주의점은 new로 메모리를 할당할 때, int* I = new I[100]; 과 같이 배열로 할당받았다면
메모리를 해제할 때 delete가 아닌 delete[]로 해주어야 한다.
delete는 메모리 영역을 통채로 해제하기 때문에, 소멸자를 단 1회만 실행한다.
(배열이 아니라면, 객체가 1개만 있는 것이 정상적인 상황이기 때문에)
반면, delete[]의 경우엔 배열 원소의 개수만큼 소멸자를 호출해준다.
원소가 10개가 있으면 10개 모두 소멸자를 호출해주는 셈이다. 배열 형식으로 동적할당을 해놓고 delete로 해제해버리면 원소중 맨 앞에있는 원소의 소멸자만 호출되고 나머지는 호출되지 않기 때문에 반드시 delete[]를 사용해야 한다.
'C++ > C++' 카테고리의 다른 글
C++ - 가변 인자 템플릿 (0) | 2024.04.18 |
---|---|
C++ - 코루틴 (coroutine) (0) | 2024.04.14 |
C++ - string_view (읽기 전용 문자열 컨테이너) (0) | 2024.04.11 |
C++ - SSO (Small String Optimization) (0) | 2024.04.11 |
C++ - placement new (0) | 2024.04.10 |