Effective C++ 读书笔记 Item28 不要返回对象内部的句柄

不要返回对象私有成员的句柄。这里的“句柄”(handle)包括引用、指针和迭代器。 这样可以增加类的封装性、使得const函数更加const, 也避免了空引用的创建(dangling handles)。

句柄就是个数字,一般和当前系统下的整数的位数一样,比如32bit系统下就是4个字节。
这个数字是一个对象的唯一标示,和对象一一对应。
这个对象可以是一个块内存,一个资源,或者一个服务的context(如 socket,thread)等等。

为了方便起见,下文中统一用指针来称呼这三类句柄。

 返回私有成员的指针

在继续Scott Meyers的讨论之前,先来回顾一下类成员指针的行为。 首先如果不加限制,直接返回私有成员的指针会导致私有成员被完全暴露。例如:

class Rectangle {
  int _left, _top;
public:
  int& left() { return _left; }
};
Rectangle rec;
rec.left() = 3;   // rec._left被修改

其实这已经足以说明返回私有成员指针相当于完全暴露了私有成员。

暴露私有成员为常量

如果是为了保护私有成员不被修改,只是为了让外界可以不通过函数就可以访问_left, 可以将left()声明为const

int& left() const{ return _left; }

上述代码还有问题~ 编译器会产生如下错误:

error: binding of reference to type 'int' to a value of type 'const int'
      drops qualifiers

这是因为常量方法不能修改当前对象,其返回值也应该是const的。应该这样写:

const int& left() const{ return _left; }

由于返回值声明了const,客户修改内部变量便会编译错了。我们成功地在开放了内部变量的同时防止了内部变量被修改。但是问题没有到此为止!还记得吗?C++的常量定义为"bitwise constness",只要当前对象没被修改就算常量。所以如果我们将left和top存在类的外面,常量方法的返回值类型检查便会失效,比如把数据存在Point里面:

class Point{
public:
    int left, right;
};
class Rectangle {
    Point* p;
public:
    int& left() const { return p->left; }
};
...
const Rectangle rec;
rec.left() = 3;     // rec明明是const对象,但我们可以修改它~

现在Rectangle的大小是sizeof(void*)(指针大小), 即p->left并不在这个对象的内存里, 因而常量方法的返回值可以不声明const。 这时客户便可以通过这个返回的left来修改对象私有成员了。 所以返回对象内部的指针,会导致常量方法的const属性被破坏。 根本原因在于C++的"bitwise constness"语法检查风格。
注意如果p被声明为Point而非Point*时,p->left仍处于当前类的内存区域内, 编译器会要求left() const返回值为const int&。

空悬指针问题

可能你已经注意到了,我们完全可以稍微改善一下上面的代码来实现私有成员的写保护。 既然是由于"bitwise constness"编译器不提供类型检查, 我们手动限制返回值为const即可:

const int& left() const{ return p->left; }

很多情况下问题确实是这样解决的,比如实现operator[]() const时。 但下标运算符只是一个特例,一般情况我们是不会返回内部指针的。 因为返回的指针和拥有者对象具有同样的生命周期, 返回的指针很容易被悬空,比如有一个返回Rectangle的bounding函数:

我们希望获得那个Rectangle的left,可能会这样写:

const int& left = bounding().left();

问题在哪里呢?left被悬空了,它并没有保持left的值! 因为bounding()返回的对象没有赋值给任何变量它是一个临时对象。 临时对象在语句执行后立即销毁,那个私有成员的引用也将失效。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值