C++ 하는 사람들은 웬만하면 알고있겠지만 적어 봄


1. malloc

 C에서는 힙 메모리 할당을 위해 malloc을 호출해서 운영체제에 메모리 공간을 요청하고 그 포인터를 반환 받는다. 하지만 이는 임의의 메모리 공간을 운영체제로부터 할당 받는 것 뿐이지, 이를 객체 생성이 아니다. malloc을 사용한다고 해서, 자동으로 생성자가 호출되는 것이 아니기 때문이다.


2. new

 객체를 생성하는 연산자이지만, 최근에는 잘 사용되지 않는데, 메모리 해제를 잊는 경우와 메모리 해제 실수가 잦기 때문이다. 그래서 어떤 언어들은 GC를 도입하기도 하고, Rust의 경우에는 Ownership 추적을 통해 컴파일 타임에 누수를 잡기도 한다. 이는 C++도 마찬가지인데 최신 C++에서는 shared_ptr, unique_ptr, weak_ptr의 사용을 권장한다. 


2.1. new의 문제점 - 예외 안정성

 shared_ptr<T> foo(new T())보다 make_shared<T>()를 사용하는 이유는 예외 안정성 때문이다. 다음과 같은 함수가 있다고 생각하자.

    void foo(shared_ptr<A> a, shared_ptr<A> b);

그리고 아래와 같이 호출한다고 하자.

    foo(shared_ptr(new A()), shared_ptr(new A()));

만약 하나를 생성하다 예외가 발생하면, 그것만 할당 해제되고 나머지는 해제되지 않는다.

(이 문제는 C++17에서 해결되었다. 그렇지만 구형 컴파일러를 사용할 수 밖에 없는 환경이라면..?)


2.2. new의 문제점 - 스마트 포인터 사용에서의 문제

    A* a = new A();

    unique_ptr<A> a1(a);

    unique_ptr<A> a2(a1);

    unique_ptr<A> a3(a1); // Error!!

    unique_ptr<A> a4(a); // Expected error, but no error


new를 사용하면 raw pointer를 사용하게 되는데, 이는 잠재적으로 스마트 포인터가 잘 작동하지 않도록 만들 수 있다.


3. 대신 make_shared, make_unique 사용하기

make_unique, make_shared를 사용하면 상기 문제를 해결할 수 있다.

2.1에서는 다음과 같이 수정한다.

    foo(make_shared<A>(), make_shared<A>());

2.2에서는 다음과 같이 수정한다.

    auto a = make_unique<A>();