본문 바로가기

개발/C++

C++ RAII

new, delete 와 스마트 포인터 간 어떤 차이가 있을까 ?

 

메모리 누수 확인

CRT 라이브러리를 통해 메모리  누수를 확인해 볼 수 있다.

#include <crtdbg.h>
#include <iostream>
 
int main() {
  int *a = new int[5];
 
  std::cout << "a: " << a << std::endl;
  _CrtDumpMemoryLeaks();
 
  return 0;
}
 

위 코드에서 a에 메모리를 할당해주고 해제를 해주지 않게 되면 crt 라이브러리에서 어떤 데이터가 해제 안되었는지 알려준다.

 

new, delete

 
#include <crtdbg.h>
#include <iostream>
 
int main() {
  int *a = new int[5];
 
  delete[] a;
  _CrtDumpMemoryLeaks();
 
  return 0;
}

 

위 코드와 같이 정상적으로 메모리 할당 및 해제가 이루어 질때는 메모리  누수가 일어나지 않는다. 그러나, 할당과 해제 사이에 예외 상황이 생긴다면 어떻게 될까?

#include <crtdbg.h>
#include <iostream>
 
void thrower() { throw 1; }
 
void f() {
  int *a = new int[5];
  thrower();
  delete[] a;
}
 
int main() {
   
  try {
  f();
  }
  catch (int) {
    std::cout << "Exception caught" << std::endl;
  }
 
  return 0;
}
위와 같이 해제 이전에 예외가 발생하는 케이스를 만들어봤다.


메모리 누수가 발생한다. 이렇게 메모리를 new로 할당해주면 delete로 해제만 하면 되는 것이 아니라 그 사이에 발생할 수 있는 예외들까지 모두 고려해야 하기 때문에 매우 복잡하다. 그래서 RAII의 개념을 적용한 스마트 포인터가 사용된다. C++에서는 heap에 할당 된 자원은 위와 같이 명시적으로 해제하지 않으면 해제되지 않지만, stack에 할당 된 자원은 자신의 scope가 끝나면 메모리가 해제되고 destructor가 불린다는 원리를 이용한 것이다.

스마트 포인터

#define _CRTDBG_MAP_ALLOC
 
#include <crtdbg.h>
#include <iostream>
 
void thrower() { throw 1; }
 
class Resource {
  int* data;
 
public:
  Resource() : data(new int(5)) { std::cout << "Resource acquired" << std::endl; }
  ~Resource() {
    delete data;
    std::cout << "Resource destroyed" << std::endl;
  }
};
 
void f() {
  std::unique_ptr<Resource> res(new Resource);
  thrower();
}
 
int main() {
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
 
  try {
    f();
  }
  catch (int) {
    std::cout << "Exception caught" << std::endl;
  }
 
  return 0;
}
 

스마트 포인터를 적용해서 소멸자가 불리는지 확인하기 위해 Resource 객체를 만들어서 위와 같이 코드를 만들었다.

throw가 발생해서 함수를 벗어나게 되더라도 f 함수의 스코프를 벗어나기 때문에 Resource 객체 메모리가 해제되는 것을 확인할 수 있다.

'개발 > C++' 카테고리의 다른 글

QNX 에서 Google Test 돌리기  (0) 2023.12.22
C++ 에서 Google Test 사용  (0) 2023.12.22