假设程序涉及矩形。每个矩形由其左上角和右下角表示。为了让Rectangle对象尽可能小,可能把定义矩形的点放在一个辅助的struct内再让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 {
public:
Rectangle(Point p1, Point p2) {};
Point& upperLeft() const {
return pData->ulhc;
}
Point& lowerRight() const {
return pData->lrhc;
}
private:
std::tr1::shared_ptr<RectData> pData;
};

这样的设计可以通过编译,但却是错误的。实际上自相矛盾,一方面upperleft和lowerRight被声明为const,不让客户修改Rectangle。另一方面,这两个函数都返回reference指向private数据,调用者可以通过这些reference更改内部数据:

     Point c1(, );
Point c2(, ); const Rectangle rec(c1, c2);
rec.upperLeft().setX(); // 现在rec变成从(50,0)到(100,100)

这里需要注意:upperLeft的调用者能够使用被返回的reference(指向rec内部的Point成员变量)来更改成员。但rec其实应该是不可变的(const)!

第一,成员变量的封装性最多只等于“返回其reference”的函数的访问级别。

第二,如果const成员函数传出一个reference,后者所指数据与对象自身有关,而它又被存储在对象之外,那么这个函数的调用者可以修改那笔数据。这正是bitwise constness的一个附带结果,条款3。

如果它们返回的是指针或迭代器,相同的结果还会发生,原因相同。reference、指针和迭代器统统都是所谓的handles(号码牌,用来取得某个对象),而返回一个“代表对象内部数据的handle”,随之而来的便是“降低对象封装性”的风险。同时,也可能造成“虽然调用const成员函数却造成对象状态被更改”。

通常我们认为,对象的“内部”就是指它的成员变量,其实不被公开使用的成员函数(protected或private)也是对象“内部”的一部分,所以也不该返回它们的handles。否则,它们的访问级别就会提高到返回它们的成员函数的访问级别。

上述两个问题可以在它们的返回类型上加上const即可:

 class Rectangle {
public:
Rectangle(Point p1, Point p2) {};
const Point& upperLeft() const {
return pData->ulhc;
}
const Point& lowerRight() const {
return pData->lrhc;
}
private:
std::tr1::shared_ptr<RectData> pData;
};

即使这样,返回“代表对象内部”的handles,有可能在其他场合导致dangling handles(空悬的号码牌):这种handle所指东西(的所属对象)不复存在。这种“不复存在的对象”最常见的来源就是函数返回值。例如某个函数返回GUI对象的外框,这个外框采用矩形形式:

 class GUIObject{...};
const Rectangle boundingBox(const GUIObject& obj);

现在,客户有可能这么使用这个函数:

 GUIObject *pgo;
...
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft()); //取得一个指针指向外框左上点

对boundingBox的调用获得一个新的、暂时的Rectangle对象,这个对象没有名称,权且称它为temp。随后upperLeft作用于temp对象身上,返回reference指向temp的一个内部成分。具体指向temp的那个Point对象。但是这个语句结束之后,boundingBox的返回值,也就是我们所说的temp,将被销毁,而那间接导致temp内的Points析构。最终导致pUpperLeft指向一个不再存在的对象,变成空悬、虚吊(dangling)!

只要handle被传出去了,不管这个handle是不是const,也不论返回handle的函数是不是const。这里的唯一关键是暴露在“handle比其所指对象更长寿”的风险下。

◆总结

1.避免返回handles(包括reference、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生"虚吊号码牌(dangling handles)"的可能性降至最低。

[Effective C++ --028]避免返回handles指向对象内部成分的更多相关文章

  1. Effective C++ -----条款28:避免返回handles指向对象内部成分

    避免返回handles(包括reference.指针.迭代器)指向对象内部.遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handle ...

  2. 【28】避免返回handles指向对象内部成分

    1.为什么? 很简单,你指向箱子里面的一个物品,使用这个物品.但是箱子不受你控制,箱子销毁了,里面的物品也会随之销毁.那么这种情况下,你指向的就是一堆垃圾,你还在使用这个物品,导致未定义的行为.

  3. [EffectiveC++]item28:避免返回handles指向对象内部成分

    可以先参考一个帖子:http://bbs.csdn.net/topics/390731394?page=1

  4. Effective C++:条款28:避免返回 handles 指向对象内部成员

    (一) 有时候为了让一个对象尽量小,能够把数据放在另外一个辅助的struct中,然后再让一个类去指向它.看以下的代码: class Point { public: Point(int x, int y ...

  5. effective c++:dynamic_cast,避免返回handles指向对象内部

    关于dynamic_cast 假定我们有一个基类指针bp,我们在运行时需要把它转换成他的派生类指针,这个时候需要用到dynamic_cast. Derived *dp = dynamic_cast&l ...

  6. 条款28:避免返回handles指向对象内部的成分(Avoid returning "handles" to objects internals)

    NOTE: 1.避免返回handles(包括references 指针 迭代器)指向对象内部.遵守这个条款可增加分装性,帮助const 成员函数的行为像个const,并将发生“虚吊号码牌”(dangl ...

  7. 读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分

    举个例子: class Student { private: int ID; string name; public: string& GetName() { return name; } } ...

  8. 条款28:避免返回handles指向对象的内部成分。

    首先看看下面这个例子: class Point{ public: point(int x, int y); ... void setX(int newVal); void setY(int newVa ...

  9. 读书笔记 effective c++ Item 28 不要返回指向对象内部数据(internals)的句柄(handles)

    假设你正在操作一个Rectangle类.每个矩形可以通过左上角的点和右下角的点来表示.为了保证一个Rectangle对象尽可能小,你可能决定不把定义矩形范围的点存储在Rectangle类中,而是把它放 ...

随机推荐

  1. OC中两种单例实现方式

    OC中两种单例实现方式 写在前面 前两天探索了一下C++ 的单例,领悟深刻了许多.今天来看看OC中的单例又是怎么回事.查看相关资料,发现在OC中一般有两种实现单例的方式,一种方式是跟C++ 中类似的常 ...

  2. SQL合并时间段的问题

    假设表结构如下所示: Tsheets 字段名 字段类型 约束 id CHAR(10) PRIMARY KEY start_date DATE CHECK(start_date<= end_dat ...

  3. andriod的简单用法2

    1.在Activity中使用menu //创建菜单项 public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this ...

  4. vi--文本编辑常用快捷键之光标移动

    再来一发! 上一篇关于vi/vim的文章中,主要介绍了文本的复制粘贴删除替换等操作,在慢慢的适应vim的过程中,我发现有很多时间实际上是浪费在移动光标上的,特别是行内移动光标.这篇文章就主要是介绍vi ...

  5. Cloudera 建议使用 NTP 使 Hadoop 群集实现时间同步

    主机的 NTP 服务未响应时钟偏差请求. 建议 这是主机运行状况测试,用于检查主机的系统时钟是否与其 NTP 服务器不同步.该测试能检查“ntpdc -c loopinfo”命令报告的主机时钟偏差绝对 ...

  6. Java设计模式系列之迭代器模式

    迭代器模式定义 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. 迭代器模式的角色构成 (1)迭代器角色(Iterator):定义遍历元素所需 ...

  7. codeforces 631A Interview

    A. Interview time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  8. Educational Codeforces Round 13 D. Iterated Linear Function (矩阵快速幂)

    题目链接:http://codeforces.com/problemset/problem/678/D 简单的矩阵快速幂模版题 矩阵是这样的: #include <bits/stdc++.h&g ...

  9. Objective-c setObject:forKey:和setValue:forKey:的区别

    setObject:forKey: 是NSMutableDictionary类的方法                               key参数类型可以是任意类型对象           ...

  10. 在Windows7上搭建Cocos2d-x 3.2alpha0开发环境

    在windows7上搭建COCOS2D-X开发环境并不难, 但是由于框架更新过快,很多用户都有困难.我希望你们认为这个教程有用. 建议:为了避免安全相关的问题,请以管理员权限执行所有的操作,当运行命令 ...