Handle 이란 다른 객체에 손을 댈 수 잇게 하는 매개자를 말하며 참조자(&), 포인터(*), 반복자(iterator) 가 있다.
사용자 정의 타입을 전달할 때는 값에 의한 전달보다 참조에 의한 전달방식을 쓴느 편이 더 효율적이라고 누군가가 부르짖은것 같다. 하지만 private 영역에 있는 data의 handle을 전달하면 외부에서 수정이 가능해진다.
class Point { public: Point(int x, int y); void setX(int newVal); void setY(int newVal); ... }; struct RectData { Point upperLeft; Point lowerRight; }; class Rectangle { public: Point& upperLeft() const { return pData->upperLeft; } Point& lowerRight() const { return pData->lowerRight; } private: std::tr1::shared_ptr<RecData> pData; }
위와 같이 되어 있는 경우
Point p1(0,0); Point p2(100,100); const Rectangle rec(p1, p2); rec.upperLeft().setX(50);
이 코드로 실행이 가능하며, p1의 x 좌표는 50으로 바뀌게 된다.
우리는 여기서 두 가지 교훈을 얻을 수 있다.
첫째, Class 데이터 멤버는 아무리 숨겨봤자 그 멤버의 참조자를 반환하는 함수들의 최대 접근도에 따라 캡슐화 정도가 정해진다.
둘째, 어떤 객체에서 호출한 상수 멤버 함수의 참조자 반환 값의 실제 데이터가 그 객체의 바깥에 저장되어 있다면, 이 함수의 호출부에서 그 데이터의 수정이 가능하다는 점이다.
해결 방법은 간단하다. 반환 타입에 const 키워드만 붙여주면 된다.
class Retangle { ... const Point& upperLeft() const { return pData->upperLeft; } const Point& lowerRight() const { return pData->lowerRight; } ... };
그래도 아직 찝찝하다. 무효참조 핸들(dangling handle) 문제가 남가 있다.
const Rectangle boundingBox(const GUIObject& obj); const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
boundingBox 함수를 호출하면 Rectangle의 임시 객체가 새로 만들어지고, 이 임시 객체의 upperLeft()가 호출되어 pUpperLeft의 포인터에 그 주소가 대입된다. 하지만 이문장이 끝날 무렵, 만들어진 Rectangle의 임시 객체는 소멸된다. 즉 pUpperLeft에 객체를 달아 줬다가 주소값만 남기고 몽땅 빼앗아 간다는 말이다.
댓글 없음:
댓글 쓰기