KoreanFoodie's Study
Effective C++ | 항목 28 : 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자 본문
Tutorials/C++ : Advanced
Effective C++ | 항목 28 : 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
GoldGiver 2022. 10. 25. 16:24
C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!
항목 28 : 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
핵심 :
어떤 객체의 내부요소에 대한 핸들(참조자, 포인터, 반복자) 를 반환하는 것은 되도록 피하자. 캡슐화 정도를 높이고, 상수 멤버 함수가 객체의 상수성을 유지한 채로 동작할 수 있도록 하며, 무효참조 핸들이 생기는 경우를 최소화할 수 있다.
클래스 내부의 데이터를 참조자로 반환하는 것은 주의해야 한다. 다음 예시를 보자.
clas Point
{
public:
Point(int x, int y);
...
void setX(int newVal);
void setY(int newVal);
...
};
struct RectData
{
Point ulhc; // upper left-hand corner
Point lrhc; // lower right-hand corner
};
class Rectangle
{
public:
// !참조자로 Point 객체를 반환!
Point& upperLeft() const { return pData->ulhc; }
Point& lowerRight() const { return pData->lrhc; }
private:
std::shared_ptr<RectData> pData;
};
...
Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2);
// 상수 객체인 rec 의 내부 데이터를 수정했다!
rec.upperLeft().setX(50);
Rectangle 클래스의 upperLeft 함수와 lowerRight 함수를 보면, Point 를 참조자로 반환하고 있다. 이 때문에, 상수 객체인 rec 의 데이터 멤버의 값이 수정이 된다.
ulhc 와 lrhc 는 private 으로 선언되어 있는데, 실질적으로는 참조자를 반환하는 함수로 인해 수정이 가능해지므로, public 변수나 다를 바가 없어진다. 이는 참조자 말고도 포인터나 반복자를 반환하는 경우에도 비슷한 문제가 발생할 것이다.
위 문제는 사실 다음과 같이 간단하게 해결할 수는 있다.
class Rectangle
{
public:
// 참조자로 Point 객체를 반환하지만, const 임
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
...
};
하지만 위처럼 핸들을 반환하는 경우는, 실제 핸들이 가리키는 녀석이 사라지는 무효참조 핸들(dangling handle) 문제가 발생할 수 있다.
class GUIObject { ... };
const Rectangle boundingBox(const GUIObject& obj);
GUIObject *pgo;
...
const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft());
위의 경우, boundingbox 는 Rectangle 임시 객체(temp 라고 부르자)를 만든다. 이 temp 객체는, uppderLeft 를 통해 ulhc 를 뱉어 내고, 해당 값의 주소를 pUpperLeft 에게 전달한다. 하지만 boundBox 를 호출하는 문장이 끝날 때 temp 객체는 파괴되기 때문에, pUpperLeft 는 실제 객체가 날라간 주소 값을 가지고 있게 된다!
핸들을 반환하는 멤버 함수가 필요한 경우도 있다(operator[] 연산자 등). 하지만 이런 경우는 예외적인 경우이므로, 일반적으로는 '핸들'을 반환하는 코드는 '되도록 피하도록' 하자.
'Tutorials > C++ : Advanced' 카테고리의 다른 글
Effective C++ | 항목 30 : 인라인 함수는 미주알고주알 따져서 이해해 두자 (0) | 2022.10.25 |
---|---|
Effective C++ | 항목 29 : 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자! (0) | 2022.10.25 |
Effective C++ | 항목 27 : 캐스팅은 절약, 또 절약! 잊지 말자 (0) | 2022.10.25 |
Effective C++ | 항목 26 : 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자 (0) | 2022.10.25 |
Effective C++ | 항목 25 : 예외를 던지지 않는 swap 에 대한 지원도 생각해 보자 (0) | 2022.10.25 |
Comments