封裝是物件導向基石,但回傳內部構件的控制代碼(引用、指標、迭代器)會悄無聲息地摧毀封裝與 const 語義,並引發懸空控制代碼風險。
class Point { public: void setX(int); void setY(int); /*...*/ };
struct RectData { Point ulhc; Point lrhc; };
class Rectangle {
shared_ptr pData;
public:
Point& upperLeft() const { return pData->ulhc; } // 錯誤!
Point& lowerRight() const { return pData->lrhc; } // 錯誤!
};
private 資料透過 public 函數回傳非 const 引用實際公開const Rectangle 物件仍可被修改
const Rectangle rec(Point(0,0), Point(100,100));
rec.upperLeft().setX(50); // 編譯通過!邏輯上修改了 const 物件
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
允許讀取、禁止寫入。封裝放寬為受控的唯讀存取。
即使回傳 const 引用,仍面臨生命週期陷阱:
const Rectangle boundingBox(const GUIObject& obj); // 回傳暫時物件
GUIObject* pgo = ...;
const Point* pUpperLeft = &boundingBox(*pgo).upperLeft(); // 危險!
// boundingBox() 回傳暫時物件 temp
// upperLeft() 回傳 temp 內部 Point 引用
// 陳述式結束時 temp 銷毀,pUpperLeft 變懸空指標
根本原因:控制代碼存活期超過其綁定的物件。與 const、控制代碼型別無關。
vector::operator[] 回傳元素引用,生命週期綁定容器本身。此屬特例而非慣例——容器與元素同生共死,設計上刻意允許。
const 引用/指標,或傳回值副本(C++11 移動語義低成本)const 控制代碼,文件化生命週期契約,考慮 shared_ptr 共享擁有權避免回傳物件內部構件的控制代碼(引用、指標、迭代器)。這能強化封裝、確保 const 語義正確、最小化懸空控制代碼風險。