사각형 클래스로부터 시작해보자.
메모리 효율을 높이기 위해 꼭짓점(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를 붙여 상태변경을 금지시키는 방법이있다.
하지만 이 방법도 여전히 불안정한 면이 있다.
무효참조 핸들이 그 문제점이다.
무효참조 핸들이란.. 그 핸들이 가리키는 데이터를 따라갔을때 해당 데이터가 없는 것이다.
주로 함수가 객체를 값으로 반환할 경우에 가장 흔하게 발생한다.
이것만은 잊지 말자!
- 객체의 내부요소(데이터 멤버 뿐 만아니라 멤버함수도 포함)에 대한 핸들을 반환하는것을 최대한 피하자. 이를 통해 캡슐화 정도를 높이고, 상수멤버함수가 그 상수성을 유지한채로 동작할수 있도록해야하며, 무효참조 핸들이 생기는 일을 최소화 할 수 있다.
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 30. 인라인 함수는 미주알고주알 따져서 이해해 두자 (0) | 2020.03.06 |
---|---|
항목 29. 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자 (0) | 2020.03.05 |
항목 27. 캐스팅은 절약, 또 절약! 잊지 말자 (0) | 2020.02.03 |
항목 26. 변수 정의는 늦출 수 잇는 데까지 늦추는 근성을 발휘하자 (0) | 2020.01.15 |
항목 25. 예외를 던지지 않는 swap에 대한 지원도 생각해보자 (0) | 2020.01.13 |
댓글