본문 바로가기
책 정리/Effective C++ 3rd

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

by ocean20 2020. 2. 4.

사각형 클래스로부터 시작해보자.

메모리 효율을 높이기 위해 꼭짓점(Point)을 별도의 구조체(RectData)에 넣어 사각형클래스(Rectangle)가 이를 가리키도록 해보자.

 

class Point{
public:
  Point(int x, int y);
  void setX(int newVal);
  void setY(int newVal);
};

struct RectData{
  Point ulhc ; // 좌측상단
  Point lrhc ; // 우측하단
};

class Rectangle {
private:
  std::tr1::shared_ptr<RectData> pData;
public:
// 아래 두행은 전부 상수멤버함수임을 기억하자.
  Point & upperLeft() const { return pData->ulhc; } // 좌측상단 값 get
  Point & lowerRight() const { return pData->lrhc; } // 우측하단 값 get
};

 

해당 샘플을 확인해보면 상수멤버함수 upperLeft(), lowerRight() 선언하였다.  상수 객체함수를 통해 호출객체가 수정되지 않게하도록( = 상수성을 유지하기위해)이 그 이유이다.

 

하지만, 이는 현재 모순덩어리의 코드이다..

 

아래와 같이 사용하면 상수 객체가 참조하는 데이터가 수정되버린다.

Point coord1(0,0); Point coord2(100,0);
const Rectangle rec(coord1, coord2);
rec.upperLeft().setX(50); // 수정됨..

 

여기서 2가지 교훈 얻을 있다.

(1) 데이터 멤버 ( ex. ulhc, lrhc ) 아무리 숨겨도, 멤버의 참조자를 반환하는 함수(ex. upperLeft()는 public이다. ->데이터멤버의 접근도가 private이라도 해당함수로 인해 접근도가 변경된다.)들의 접근도에 따라 캡슐화 정도가 정해진다.

(2) 어떤 객체에서 호출한 상수 멤버함수(ex. Point& upperLeft() const ) 참조자 반환값의 실제데이터가 해당객체의 바깥에 저장되어 있다면.. 이함수의 호출부에서 데이터가 수정이 가능하다는

 

이러한 교훈을 통해 핸들(다른 객체에 손댈수 있는 매개자 : 참조자, 포인터, 반복자) 반환하는 코드는 최대한 피하자.

 

해당 문제를 해결하는 방법 - 상수멤버함수 upperLeft() lowerRight() 반환타입에 const 붙여 상태변경을 금지시키는 방법이있다.

 

하지만 방법도 여전히 불안정한 면이 있다.

 

무효참조 핸들 문제점이다.

무효참조 핸들이란.. 핸들이 가리키는 데이터를 따라갔을때 해당 데이터가 없는 것이다.

 

주로 함수가 객체를 값으로 반환할 경우에 가장 흔하게 발생한다.

 

이것만은 잊지 말자!

  - 객체의 내부요소(데이터 멤버 만아니라 멤버함수도 포함) 대한 핸들을 반환하는것을 최대한 피하자. 이를 통해 캡슐화 정도를 높이고, 상수멤버함수가 상수성을 유지한채로 동작할수 있도록해야하며, 무효참조 핸들이 생기는 일을 최소화 있다.

댓글