条款28:避免返回handles指向对象内部成分
在讨论今天这个条款之前,我们先来看看如下代码:
class Point{
public:
Point(int x,int y);
...
void setX(int newVal);
void setY(int newVal);
...
};
struct RectDate{
Point rlhc;
Point lrhc;
};
class Rectangle{
public:
...
Point& upperLeft() const
{
return pData->ulhc;
}
Point& lowerRight() const
{
return Data->lrhc;
};
...
private:
std::trl::shared_ptr<RectData> pData;
};
这样的函数编译没有任何问题,但是逻辑上确实不对!我们可以看一下upperLeft和lowerRight函数被声明为const成员函数,因为它们的目的只是为了提供客户一个得知Rectangle相关坐标点的方法,而不是让客户修改Rectangle,但是另外一个方面,返回的是references指向的是Rectangle内部的private数据,但Rectangle内部的private数据却可以调用自身内部的public函数,导致
调用者可以获取这些references进而改变内部数据!导致不安全性出现啦!
那么我们怎样可以解决这个问题呢?
看看书中的解决办法吧!
class Rectangle{
public:
...
const Point& upperLeft()const
{
return pData->ulhc;
}
const Point& lowerRight()const
{
return pData->lrhc;
}
...
};
有了这样的改变,客户就可以取得举行的Points,但不能修改它们吧!
PS:引用(References)、指针和迭代器统统都是所谓的handles(号码牌,用于取得某个对象),而返回一个“代表对象内部数据”的handle,随之而来的是“降低对象封装性”的风险;同时也可以导致像上面所提到的虚假const信息。
有时候,返回handles有可能在其他场合带来问题,更明确的说,可能导致dangling handles(空悬的号码牌),具体见如下代码:
class GUIObject{...};
const Rectangle boundingBox(const GUIObject& obj);
GUIObject* pgo;
...
const Point* pUpperLeft=&(boundingBox(*pgo).upper());
对boundingBox的调用获得一个新的、暂时的Rectangle对象,这个对象那个没有名称,我们称之为temp,然后让upperLeft函数作用于temp身上,返回一个指向temp的内部成分的引用,于是pUpperLeft指向那个对象,当upperLeft()函数结束之后,temp将被销毁,于是temp内的Points析构,进而pUpperLeft指向一个不复存在的对象那个,此时pUpperLeft变成悬吊的对象!
总结:避免返回handles(引用,指针、迭代器)指向对象内部,可以保证类的封装性,并将可能发生“dangling handles”的可能性降至最低!