[Effective C++] 항목 14 ~ 17
항목 14 : 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자.
-
힙에 생기지 않는 자원은 스마트 포인터로 처리해 주기엔 맞지 않다.
- RAII 법칙(생성시 자원 획득, 소멸시 자원 해제)을 따르는 클래스의 복사될 때 동작법
- 복사를 금지한다 : 복사되면 안되는 RAII 클래스는 반드시 복사 함수 막을 것.
- 관리하고 있는 자원에 대해 참조 카운팅 수행 : shared_ptr을 데이터 멤버로 넣는다.
- shared_ptr은 기본적으로 참조 카운트가 0이 되면 대상을 삭제하지만 두번째 매개변수로 삭제자 지정을 할 수 있다.(auto_ptr은 없음)
- shared_ptr이 삭제를 맡으므로 소멸자는 선언하지 않아도 됨.
- 관리하고 있는 자원을 진짜로 복사한다 : 깊은 복사를 수행할 것
- 관리하고 있는 자원의 소유권을 옮긴다 : auto_ptr의 복사 동작
- 객체 복사 함수는 컴파일러에 의해 생성될 여지가 있으므로 필요시 직접 만들 수밖에 없다.
항목 15 : 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자.
- RAII 클래스의 객체를 그 객체가 감싸고 있는 실제 자원으로 변환해야 할 때가 존재.
- 명시적 변환 : get()같은 멤버 함수 제공. 사용자가 하부 수준 API를 쓰고 싶을 때마다 get을 호출해야 함.
- 암시적 변환 : operator()를 사용. 자원관리 클래스를 쓰려 한 부분에서 원하지 않는 타입 변환이 일어날 염려가 있다.
- 제대로 만들어진 스마트 포인터 클래스는 거의 모두 포인터 역참조 연산자(operator->, operator*)도 오버로딩하고 있다.
항목 16 : new 및 delete를 사용할 때는 형태를 반드시 맞추자
- new 연산자를 통해 표현식을 꾸미게 되면
- 메모리 할당(operator new 함수가 쓰인다)
- 할당된 메모리에 대해 한 개 이상의 생성자 호출
- delete 표현식을 쓸 경우
- 할당된 메모리에 대해 한 개 이상의 소멸자 호출
- 메모리 해제(operator delete 함수가 쓰인다)
- 단일 객체의 메모리 배치 구조(layout)는 객체 배열에 대한 메모리 배치 구조와 다르다.
- 한 개의 객체 : [ object ]
- 객체의 배열 : [ n ][ object ][ object ][.. »- 배열의 크기 정보가 있다.
-
new 표현식에 []를 썼으면 delete 표현식에도 []를 써야 한다!
- 동적 할당 메모리에 대한 포인터를 멤버 데이터로 가지는 클래스의 생성자가 여러개일 경우 생성자의 new를 모두 같게 맞춰야 소멸자의 delete와 쌍을 맞출 수 있다.
항목 17 : new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 맞추자
- C++ 컴파일러는 다른 언어(C# 또는 자바)와 달리 매개변수의 평가 순서가 자유롭다.
- 컴파일러마다 매개 변수 평가 순서가 다를 수 있다.
- 문제가 발생할 수 있는 예시
- ex)
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority(1));
- ex)
- 순서 예 1.
- priority 호출
- new Widget 실행
- tr1::shared_ptr 생성자 호출
- 순서 예 2.
- new Widget 실행
- priority 호출 -> 여기서 예외 발생시 Widget 자원 누수
- tr1::shared_ptr 생성자 호출
- new로 생성한 객체를 스마트 포인터로 넣는 코드를 별도의 한 문장으로 만들자