자원 - 사용을 마치고 난 후에는 시스템에 돌려주어야 하는 모든 것을 말함.
Item 13 자원관리에는 객체가 그만!
펙토리함수 : 어떤 객체에대한 포인터를 손에 얻는 용도
어떤 함수안에서 펙토리함수를 사용하여 객체에 대한 포인터를 얻은뒤, 그 객체를 사용하다가 문제가 발생하면 예외가 발생할수 있음. 예외가 발생하게 되면 delete문을 건너뛸수도 있다. 그럴 경우 메모리 릭이 생기게 됨.
펙토리함수로부터 얻어낸 자원이 항상 해제되도록 만들 방법은 자원은 객체에 넣고 그 자원 해제는 소멸자가 맡도록 하고, 함수를 떠날때 호출되도록 만들게 한다. 스마트포인터!!
1 | 자원을 획득한 후에 자원관리 객체에게 넘긴다. |
Item 14 자원 관리 클래스의 복사 동작에 진지하게 고찰하자.
RAII 객체가 복사될때 어떤 일이 일어나야되는가? 해당자원이 고유적인데, 객체가 복사된다면 소멸자 호출시에 그 객체는 어떻게 될까?
선택 가능한 복사동작에 대한 조건들
- 복사를 금지(
unique_ptr
)
RAII 객체가 복사되도록 놔두는 것은 말이 안된다. - 관리하고 있는 자원에 대해 참조카운팅을 수행(
shared_ptr
)
자원을 사용하고 있는 마지막 객체가 소멸될 때까지 그 자원은 소멸을 안시키는 방식이다.shared_ptr
은deleter
(삭제자) 지정을 허용하게 되는데, 삭제자란shared_ptr
이 유지하는 참조카운트가 0이 될때 호출되는 함수나 함수 객체를 말한다. - 관리하고 있는 자원을 진짜로 복사합니다.
swallow copy
가 아닌deep copy
로써, 새롭게 만들어 복사한다. - 관리하고 있는 자원의 소유권을 옮긴다.
Item 15 자원관리 클래스에서 관리되는 자원은 외부에서 접근할수 있도록 하자.
1 | std::shared_ptr<Investment> pInv (createInvestment()); // Investment* createInvestment(); 의 함수 |
dayHeld
함수는 Investment*
를 원하는데 shared_ptr<Investment>
이므로 에러가 난다.
이로써 변환할 방법이 필요해지는데, 명시적 변환과 암시적 변환이 있음.
명시적 변환으로는 get
함수를 사용하면된다.
암시적 변환으로는 operator->
와 operator*
을 사용한다.
1 | int test(int *pInt) |
정리
- 실제 자원을 직접 관리해야 되는 기존 API들도 많기 때문에 RAII 클래스를 만들때는 그 클래스가 관리하는 자원을 얻을수 있는 방법을 열어줘야 함.
- 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능함. 안전성만 따지면 명시적 변환이 대체적으로 더 낫지만, 편의성으로 보면 암시적 변환도 괜찮음.
Item 16. new 및 delete 를 사용할때는 형태를 맞추자.
1 | std::string *stringArr = new std::string[100]; |
delete
연산자가 적용하는 객체의 갯수는 소멸자가 호출하는 횟수와 같은데, 위의 예제방식으로는 객체 한개가 소멸하게 된다.delete[]
을 호출하게 되면 delete
는 앞쪽의 메모리 몇 바이트를 읽고 이것을 배열의 크기로 인식하고, 해당하는 횟수만큼 소멸자를 호출한다.
정리
- new 표현식에 []을 썻으면 대응되는 delete도 []을 써야된다.
Item 17. new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한문장으로 만들자.
1 | std::shared_ptr<int> pw(new int); // new로 생성한 객체를 스마트 포인터에 담는 코드를 독립적 문장으로 만듬. |