[Effective C++ --028]避免返回handles指向对象内部成分
假设程序涉及矩形。每个矩形由其左上角和右下角表示。为了让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指向对象内部成分的更多相关文章
- Effective C++ -----条款28:避免返回handles指向对象内部成分
避免返回handles(包括reference.指针.迭代器)指向对象内部.遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handle ...
- 【28】避免返回handles指向对象内部成分
1.为什么? 很简单,你指向箱子里面的一个物品,使用这个物品.但是箱子不受你控制,箱子销毁了,里面的物品也会随之销毁.那么这种情况下,你指向的就是一堆垃圾,你还在使用这个物品,导致未定义的行为.
- [EffectiveC++]item28:避免返回handles指向对象内部成分
可以先参考一个帖子:http://bbs.csdn.net/topics/390731394?page=1
- Effective C++:条款28:避免返回 handles 指向对象内部成员
(一) 有时候为了让一个对象尽量小,能够把数据放在另外一个辅助的struct中,然后再让一个类去指向它.看以下的代码: class Point { public: Point(int x, int y ...
- effective c++:dynamic_cast,避免返回handles指向对象内部
关于dynamic_cast 假定我们有一个基类指针bp,我们在运行时需要把它转换成他的派生类指针,这个时候需要用到dynamic_cast. Derived *dp = dynamic_cast&l ...
- 条款28:避免返回handles指向对象内部的成分(Avoid returning "handles" to objects internals)
NOTE: 1.避免返回handles(包括references 指针 迭代器)指向对象内部.遵守这个条款可增加分装性,帮助const 成员函数的行为像个const,并将发生“虚吊号码牌”(dangl ...
- 读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分
举个例子: class Student { private: int ID; string name; public: string& GetName() { return name; } } ...
- 条款28:避免返回handles指向对象的内部成分。
首先看看下面这个例子: class Point{ public: point(int x, int y); ... void setX(int newVal); void setY(int newVa ...
- 读书笔记 effective c++ Item 28 不要返回指向对象内部数据(internals)的句柄(handles)
假设你正在操作一个Rectangle类.每个矩形可以通过左上角的点和右下角的点来表示.为了保证一个Rectangle对象尽可能小,你可能决定不把定义矩形范围的点存储在Rectangle类中,而是把它放 ...
随机推荐
- 使用IP欺骗Loadrunner并发测试小结
测试要求: 在本次测试中,我需要并发50个User,每一个User占用一个独立的IP,并且只执行一次脚本.脚本中发起两个请求,其中第一次请求返回200后才执行第二个请求.使用win7 OS. ...
- 修改Eclipse的EasyExplore插件的键盘快捷键
工欲善其事,必先利其器 为了高效的编码,一个顺手的IDE是必不可少的. Eclipse下的EasyExplore插件挺不错,经常会打开项目的文件夹查找内容,只是日渐习惯全部键盘操作后,有时想使用Eas ...
- ASP.NET 日期 时间 年 月 日 时 分 秒 格式及转换
在平时编码中,经常要把日期转换成各种各样的形式输出或保持,今天专门做了个测试,发现DateTime的ToString()方法居然有这么多的表现形式,和大家一起分享. DateTime time=Dat ...
- offsetWidth、offsetleft 等图文详解
网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...
- C++ 我想这样用(六)
嗯,上一篇已经介绍了面向过程编程的语法知识,接下来是最后的也是最重要的一个部分: 第三部分:基于对象的编程风格 1.构造函数的两种写法 比如我们有如下的类定义: class Circle { publ ...
- Servlet学习笔记(1)--第一个servlet&&三种状态对象(cookie,session,application)&&Servlet的生命周期
servlet的404错误困扰了两天,各种方法都试过了,翻书逛论坛终于把问题解决了,写此博客来纪念自己的第一个servlet经历. 下面我会将自己的编写第一个servlet的详细过程提供给初学者,大神 ...
- POJ 1258 Agri-Net (最小生成树)
Agri-Net 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/H Description Farmer John has be ...
- Spring MVC SimpleUrlHandlerMapping example
In Spring MVC application, the SimpleUrlHandlerMapping is the most flexible handler mapping class, w ...
- poj 3026 Borg Maze (BFS + Prim)
http://poj.org/problem?id=3026 Borg Maze Time Limit:1000MS Memory Limit:65536KB 64bit IO For ...
- TQJson序列和还原clientdataset.data
TQJson是QDAC开源项目的JSON对象,其特点是跨平台,功能强大. 序列 procedure TForm2.Button2Click(Sender: TObject);var AJson: TQ ...