[Effective C++] 항목28 : 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자

Jangmanbo·2024년 4월 2일
0

Effective C++

목록 보기
28/33
class Point
{
public:
	Point(int x, int y);
    ...
    void setX(int newVal);
    void setY(int newVal);
    ...
};

struct RectData
{
	Point ulhc;			// upper left-hand corner (좌측 상단)
    Point ulhc;			// lower right-hand corner (우측 하단)
};

// 사각형 클래스
class Rectangle
{
publc:
	...
	Point& upperLeft() const { return pData->ulhc; }
    Point& lowerRight() const { return pData->lrhc; }
    ...
private:
	shared_ptr<RectData> pData;
};

여기서 upperLeft, lowerRight함수는 상수 함수인데 pData 객체에 대한 참조자를 반환하고 있다.

Point coord1(0, 0);
Point coord2(100, 100);

const Rectangle rec(coord1, coord2);
rec.upperLeft().setX(50);	// upperLeft가 (0, 0)이 아니라 (50, 0)이 된다..!

따라서 upperLeft, lowerRight의 리턴값을 통해 Rectangle 객체의 내부 데이터가 수정될 수 있다.


참조자 반환 함수로 인한 문제점

  • 클래스 데이터 멤버는 아무리 숨겨도, 그 멤버의 참조자를 반환하는 함수에 의해 캡슐화 정도가 정해진다.
    • ulhc, lrhc는 private이지만 실질적으로는 public 멤버이다. (참조자 반환 함수가 public 멤버함수이기 때문)
  • 어떤 객체에서 호출한 상수 멤버 함수의 참조자 반환 값의 실제 데이터가 그 객체의 바깥에 저장되어 있으면,
    함수 호출부에서 해당 데이터 수정이 가능하다.
    • upperLeft의 반환값을 따로 저장해두면 해당 변수를 통해 데이터 수정이 가능하다.

즉, 내부 요소에 대한 핸들(참조자, 포인터, 반복자)을 반환하는 함수는 그 객체의 캡슐화를 무너뜨린다.
(여기서 내부 요소에는 함수도 포함이다.)

해결법: 반환 타입에 const 키워드를 붙이자!

const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }

반환 타입에 const를 붙이면 상수 함수에 걸맞게, 함수의 호출부에서 객체의 상태를 바꿀 수 없다.



무효 참조 핸들 문제

class GUIObject { ... };

const Rectangle boundingBox(const GUIObject* obj);

...

GUIObject *pgo;
...
const Point *pUpperLeft = &(boudingBox(*pgo).upperLeft());
  1. boudingBox(*pgo)Rectangle 임시 객체(temp)가 생성된다.
  2. 임시 객체 temp에 대해 upperLeft를 호출하면 Point 객체 참조자가 반환된다.
  3. 이 줄이 끝날 무렵, 임시 객체 temp가 소멸되므로 Point 객체들도 사라진다.

즉, pUpperLeft가 가리키는 객체는 사라진다!

이 무효 참조 핸들 문제는 핸들의 종류, 핸들에 const가 붙었는지 여부에 상관없이 핸들을 반환하는 함수라면 발생한다.


정리

  • 어떤 객체의 내부 요소에 대한 핸들(참조자, 포인터, 반복자)을 반환하는 것을 되도록 피하자.
  • 핸들 반환 함수가 적을수록
    • 캡슐화 ↑
    • 상수 멤버 함수가 객체의 상수성을 유지
    • 무효 참조 핸들 문제를 최소화



















0개의 댓글