스토리텔링 개발자

[Effective C++] 28. 클래스 내부 private 객체에 대한 핸들 리턴 문제 본문

개발/Effective C++

[Effective C++] 28. 클래스 내부 private 객체에 대한 핸들 리턴 문제

김디트 2024. 6. 24. 11:52
728x90

항목 28. 내부에서 사용하는 객체에 대한 ‘핸들’을 반환하는 코드는 되도록 피하자.

 

 

 

참조자 반환 함수에 대한 고찰
  • 메모리 부담을 최대한 줄이기 위해 실 데이터를 가리키는 포인터를 가지고 있는 사각형 클래스를 만든다.
  • class Point
    {
    public:
        Point(int x, int y);
        ...
        void setX(int newVal);
        void setY(int newVal);
        ...
    };
    
    class RectData
    {
        Point ulhc; // 좌측 상단
        Point lrhc; // 우측 하단
    };
    
    class Rectangle 
    { 
    public: 
        .... 
        // 참조자 형태로 꼭지점 정보를 반환한다.
        Point& upperLeft() const
        { 
            return pData->ulhc; 
        } 
        Point& lowerRight() const 
        { 
            return pData->lrhc; 
        } 
        .... 
    private:
        shared_ptr<RectData> pData; // 꼭지점 정보를 가리킨다.
    }
  • 여기서 upperLeft, lowerRight 함수가 값을 참조자 형태로 반환하는 것은 잘못이다.
    • 상수 멤버 함수로 설정되어 있으나, 반환되는 참조자로 인해 외부에서 내부값을 마음대로 수정할 수 있다는 맹점이 있다.
    • 즉, pData가 아무리 private로 선언되어 있더라도, 실질적으로는 public이나 다름 없다.
    • 이는 비트 수준 상수성의 한계이다.

 

 

 

핸들
  • 다른 객체가 손을 댈 수 있게 하는 매개자
  • 참조자, 포인터, 반복자
  • 핸들을 반환하게 하면 그 객체의 캡슐화를 무너뜨리는 위험을 무릅쓸 수밖에 없다.

 

 

 

어떤 객체의 내부 요소(internals)는 공개하지 말 것
  • 내부 요소에는 데이터 멤버 뿐 아니라 일반적 수단으로 접근이 불가능한 멤버 함수도 포함된다.
    • 즉, protected 또는 private 멤버 함수 역시 내부 요소이다.
  • 따라서 외부 공개가 차단된 멤버 함수의 포인터를 반환하는 멤버 함수 역시 절대 만들면 안된다.

 

 

 

해결법
  • 참조자를 사용하면서도 공개 수준을 지키는 방법은, const 참조자로 리턴하는 것이다.
  • const Point& upperLeft() const 
    { 
      return pData->ulhc; 
    } 
    const Point& lowerRight() const 
    { 
      return pData->lrhc; 
    } 
    
  • 하지만 이 경우에도 무효 참조 핸들이 발생할 여지가 있으므로 위험 요소가 없는 것은 아니다.

 

 

 

무효 참조 핸들
  • 핸들은 유효하지만, 핸들의 실제 객체 데이터가 사라진 경우.
  • class GUIObject { ... };
    
    // Rectangle 객체를 값으로 반환한다.
    // const 가 붙는 이유는 항목 3 참조
    const Rectangle boundingBox(const GUIObject& obj);
    
    ...
    
    GUIObject *pgo;
    ...
    const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
    
    // boundingBox가 만든 임시객체는 유효범위를 벗어나면 소멸된다.
    // 허나 pUpperLeft는 해당 임시객체의 내부 요소를 핸들로 가지고 있다.
  • 일단 바깥으로 떨어져 나간 핸들은 그 핸들이 참조하는 객체보다 더 오래 살 위험이 있다는 점을 유의해야 한다.
728x90
Comments