스콧 마이어스의 Effective C++을 읽고 개인 공부 목적으로 요약 작성한 글입니다!
💡 자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하는 RAII 객체를 사용하자!
💡 일반적으로 널리 쓰이는 RAII 클래스는 tr1::shared_ptr과 auto_ptr 이다
💡 이들 중, shared_ptr가 복사시의 동작이 직관적이기 때문에 더 좋다.
auto_ptr는 복사되는 객체 (원본 객체)를 null로 만든다.
사용을 일단 마치고 난 후에는 시스템에 돌려줘야 하는 모든 것.
ex) 동적 할당한 메모리, 파일 서술자, mutex lock, font, brush, DB 연결, 네트워크 소켓 등
가져와서 다 썼으면 해제해야 한다
class Investment { ... };
Investment* createInvestment(); // Investment 계통에 속한 클래스의 객체를 동적할당하고 그 포인터를 반환
void f() {
Investment* pInv = createInvestment();
...
delete pInv;
};
createInvestment()
: 최상위 클래스인 Investment 계통에 속한 클래스의 객체를 동적할당하고, 그 포인터를 반환해주는 함수
createInvestment() 함수를 통해 얻은 객체를 다 사용했으면 이 함수의 호출자에서 삭제해줘야 한다.
-> f()
...
부분에서 return문이 있을 경우- 하나의 루프 안에 createInvestment()와 delete가 같이 있는 상황에서 continue로 루프를 빠져나올 경우
...
부분 중 어떤 문장에서 예외가 나올 경우
이 경우, delete 문이 실행되지 않는다.
결과는?
...
Investment 객체를 담고 있는 메모리가 누출되고, 객체가 갖고 있던 자원까지 누출된다...
그리고 그 소멸자는 실행 제어가 f를 떠날 때 호출되도록 하자!
자원을 객체에 넣어서, C++이 자동으로 호출해주는 소멸자에 의해 자원을 자동으로 해제하도록 하자.
자원 획득과 자원 관리 객체의 초기화가 한 문장에서 이루어진다.
RAII는 자원 관리에 객체를 사용하는 아이디어를 의미한다.
소멸자는 어떤 객체가 소멸될 때 자동적으로 호출된다.
실행 제어가 무슨 이유로 블록을 떠나는지에 관계없이, 자원 해제가 이루어진다.
가리키고 있는 대상에 대해 소멸자가 자동으로 delete를 호출해주도록 설계된 클래스.
하나의 블록, 함수로부터 실행 제어가 빠져나올 때 자원이 해제되도록 해준다.
void f() {
std::auto_ptr<Investment> pInv(createInvestment());
...
std::auto_ptr<Investment> pInv2(pInv);
pInv = pInv2;
}
이렇다고 해주자
어떤 객체를 가리키는 auto_ptr의 개수가 둘 이상이면 안된다.
자신이 소멸될 때 자신이 가리키고 있는 대상에 대해 자동으로 delete해주기 때문이다.
따라서
auto_ptr 객체를 복사하면 원본 객체는 null이 된다.
복사하는 객체만이 그 자원의 유일한 소유권을 갖는다.
위 상황에서,
pInv2를 pInv1을 통해 복사 생성했다
따라서 pInv2는 현재 createInvestment()에서 반환된 객체를 가리키고, pInv은 null이다.
pInv = pInv2
를 통해, 다시 pInv은 createInvestment()에서 반환된 객체를 가리키고, pInv2는 null이다.
어때
모든 자원에 대한 관리 객체로서 auto_ptr을 쓰는 것은 부적절하다.
STL 컨테이너들은 정상적인 복사 동작이 필요하거등..
특정한 어떤 자원을 가리키는 외부 객체의 개수를 유지하고 있다가, 그 개수가 0이 되면 해당 자원을 삭제한다.
단, 다른 두 객체가 서로를 가리키고 있는 상황에서는 ,, 어쩔 수 없다
void f() {
...
std::tr1::shared_ptr<Investment> pInv(createInvestment());
std::tr1::shared_ptr<Investment> pInv2(pInv);
pInv = pInv2;
};
이러한 상황에서, pInv와 pInv2 모두 createInvestment()에서 반환된 객체를 가리킨다.
복사가 정상적으로 동작하기 때문에 STL 컨테이너의 환경에서도 사용할 수 있다.
delete 연산자를 사용한다.
따라서, 동적 할당한 배열에 대해 auto_ptr나 shared_ptr를 사용하면 안된다.
컴파일 에러가 안나니까 조심해야 한다..
std::auto_ptr<std::string> aps(new std::string[10]);
std::tr1::shared_ptr<int> spi(new int[1024]);
둘다 잘못된 delete가 사용되니까 이렇게 하면 안된다.
근데 앞서 말했듯, 컴파일에러가 나지는 않으니까 주의해야 한다!!
동적으로 할당한 배열을 꼭,, 쓰고 싶다면
boost::scoped_array나 boost::shared_array를 사용하셈
😊 느낀점
흠
c++의 필수품,, 스마트 포인터
좀 더 정확히 알게 된 느낌?
나 저번에 이 부분,, 메모리 누수 났던 기억이 있어서 조금 더 열심히 봤다..
동적할당한 객체에 대해서는 꼭,, 스마트포인터를 쓰도록 해욥