Post List

2015년 1월 4일 일요일

내부에서 사용하는 객체에 대한 'Handle'을 반환하는 Code는 되도록 피하자.

* 어떤 객체의 내부요소에 대한 핸들(참조자, 포인터, 반복자)을 반환하는 것은 되도록 피하세요. 캡슐화 정도를 높이고, 상수 멤버 함수가 객체의 상수성을 유지한 채로 동작할 수 있도록 하며, 무효참조 핸들이 생기는 경우를 최소화할 수 있습니다.

 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에 객체를 달아 줬다가 주소값만 남기고 몽땅 빼앗아 간다는 말이다.

댓글 없음:

댓글 쓰기