[Effective C++] #27_캐스팅은 절약하고 또 절약하자!
Scott Meyers의 "Effective C++" 를 통해, C++ 구현에 필요한 개념들을 이해하고, 기록하기 위함입니다. 해당 항목은 5장 '구현', 항목 28 "내부에서 사용하는 객체에 대한 핸들을 반환하는 코드는 되도록 피하자" 에 해당하는 내용입니다.
멤버의 참조자를 반환하는 멤버 함수들의 최대 접근도
class Point {
public:
Point(int x, int y);
...
void setX(int newVal);
void setY(int newVal);
...
};
struct RecData {
Point ulhc; // upper left-hand corner
Point lrhc; // lower right-hand corner
};
class Rectangle{
public:
...
Point& upperLeft() const {return pData->ulhc}; //Private member의 참조자를 반환
Point& upperLeft() const {return pData->lrhc};
...
private:
std::tr1:shared_ptr<RecData> pData; // RecData를 가리키는 포인터.
...
};
클래스 객체 내부에 정의된 private 멤버들은 외부로부터의 수정을 금지합니다. 하지만 이들의 참조자를 반환하는 함수가 Public 멤버 함수일 경우 public 멤버처럼 사용될 우려가 있습니다. 위 코드를 살펴보면, Rectangle 객체는 객체 외부에 정의된 RecData 구조체를 가리키는 포인터를 private 멤버로 가지고 있습니다. Public 멤버 함수들은 상수 멤버 함수로, 값에 대한 정보만 사용자에게 제공하고, 객체를 수정하는 일이 없도록 하고 있죠. 하지만, Point 데이터 멤버를 참조자로 끌어와 언제든지 값을 수정 할 수 있게됩니다.
- Private 멤버들의 참조자를 반환하는 Public 멤버 함수는 이들을 Public 멤버처럼 만들 여지가 있습니다.
- 어떠한 객체에서 호출한 멤버 함수가 반환하는 참조자의 실제 데이터가 객체 외부에 정의 되어 있다면, 이 멤버 함수들을 통해 그 데이터의 수정이 가능합니다.
참조자, 포인터 및 반복자는 모두 "핸들"이고, 객체의 내부요소에 대한 핸들을 반환하게 만들면 언제든지 그 객체의 캡슐화를 위협하는 요소가 됩니다.
핸들을 사용한 캡슐화 위협에대한 해결책
class Rectangle {
...
const Point& upperLeft() const {return pData->ulhc;} // "const" 사용!
const Point& upperLeft() const {return pData->lrhc;}
...
}
핸들을 반환하는 함수에 "const 핸들"을 반환하게 만들어서, 사용자들이 Rectangle을 구성하는 Point들, 즉 private 멤버들에 대한 정보를 들여다 볼 수 있지만, 이들을 수정할 수 없게됩니다. 책에서 강조하는 더 중요한 내용은 느슨하게 만든 캡슐화에도 어느 정도 제한을 두어 설계했다는 것입니다. 예를들면, 사용자에게 읽기 권한만 주어지고, 쓰기 권한은 허가하지 않는 것입니다. 하지만, 아직도 핸들을 반환한다는 점에서 찝찝함을 갖고 있죠.
무효참조 핸들 문제
// 어떤 GUI객체
class GUIObject {...};
//GUI객체의 사각형 테두리 영역을 Rectangle객체로 반환하는 함수
const Rectangle boundingBox(const GUIObject& obj);
//사용자 예제
GUIObject *pgo;
...
const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft());
가장 마지막 라인의 boundingBox 함수 호출문을 살펴보겠습니다. boudningBox를 호출하게되면 임시의 Rectangle 객체가 반환을 위해 생성될 것 입니다. 임시 Rectangle 객체를 temp로 부르겠습니다. 함수 호출을 통해 temp 객체의 upperLeft 멤버의 참조자가 호출됩니다. 그리고, 참조자에 & 연산자가 붙어 "주소값"이 pUpperLeft 포인터에 대입됩니다. 하지만, 이 함수 호출문이 끝날 무렵, 함수의 반환 값, 즉 임시로 생성된 객체 temp는 소멸 되어, pUpperLeft 포인터가 가리킨는 객체가 날아가버립니다! 결과적으로 pUpperLeft에 객체를 가리키게 해놓고, 해당 객체를 날려버려, 주소만 남기고 사리진것 입니다. 따라서, 객체의 내부에 대한 핸들을 반환하는 함수는 어떻게든 위험하다고 할 수 있습니다. 물론 예외도 존재합니다. operator[] 현산자는 string이나 vecotr등 컨테이너에 들어 있는 원소 데이터에 대한 참조자를 반환하는 형식이죠?
객체의 내부 요소에 대한 핸들을 반환하는 것을 피하면, 캡슐화 정도를 높이고, 상수 멤버 함수의 상수성을 유지하며, 무효참조 핸들이 생기는 것을 방지 할 수 있습니다.
'언어 > Effective C++' 카테고리의 다른 글
[Effective C++] #30_인라인 함수 (0) | 2021.12.30 |
---|---|
[Effective C++] #29_예외 안전성 확보 (0) | 2021.12.28 |
[Effective C++] #27_캐스팅 (0) | 2021.12.27 |
[Effective C++] #26_변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자 (0) | 2021.12.27 |
[Effective C++] #25_예외를 던지지 않는 swap에대한 지원 (0) | 2021.12.25 |