IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《Effective C++》学习笔记(条款28:避免返回handles指向对象内部成分) -> 正文阅读

[C++知识库]《Effective C++》学习笔记(条款28:避免返回handles指向对象内部成分)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!

假如我们写一个表示矩形的类Rectangle,这个矩形由左上角和右下角的顶点坐标表示。为了表示这两个点,我们写一个表示点的类:

class Point{   
public:
    Point(int x, int y);
    void setX(int newVal);
    void setY(int newVal);
  	... 
};

为了矩形类尽可能小,把两个点放在一个 struct 内,再让 Rectangle 指向它:

struct RectData{
    Point ulhc; //左上角upper left-hand corner   
    Point lrhc; //右下角lower right-hand corner 
};  

class Rectangle{
  	...   
private:     
    shared_ptr<RectData> pData; //智能指针见第条款13
};

Rectangle 能够计算它自身的范围,所以需要提供接口访问它左上角和右下角的坐标。Point 是自定义类型,所以根据条款20给我们的建议:以引用传递方式传递用户自定义类型往往比值传递高效),这些函数于是返回引用,代表底层的 Point 对象:

class Rectangle{
public:
    ...
    Point& upperLeft() const {return pData->ulhc;}
    Point& lowerRight() const {return pData->lrhc;}
    ... 
};

这样的设计可以通过编译,但确实错误的。实际上它是自我矛盾的。因为 upperLeft()lowerRight() 被声明为 const 成员函数,目的是为了调用者只能读取它,不能修改它,但是两个函数返回引用指向 private 内部数据,调用者可通过这些引用修改内部数据。例如:

Point coord1(0,0);
Point coord2(100,100); 
const Rectangle rec(coord1,coord2); //我们希望它是一个const对象 
rec.upperLeft().setX(50);  			//现在通过引用能改变const对象的状态!

这带给我们两个教训:

第一,成员变量的封装性最多只等于“返回其引用”的函数的访问级别。本例中的 ulhclrhc 都被声明为 private,它们实际上却是 public ,因为 public 函数 upperLeft()lowerRight()传出了它们的引用。

第二,如果一个函数返回了指向储存在对象外部的数据成员的引用,即使这个函数声明为了const,调用这个函数的人也能修改这个成员(见条款3 bitwise constness 的局限性)

除了引用,返回指针和迭代器的结果也是一样的,原因也一样。引用,指针,迭代器都是所谓的"句柄"(handle),即接触对象的某种方式。直接返回句柄总会带来破坏封装的风险,这也导致声明为 const 的函数并不是真正的 const。

还要注意的是,"内部成员"除了内部数据还包括内部函数,即声明为私有(private)或保护(protected)的函数,因此对于内部函数也是一样,不要返回它们的句柄,否则用户也可以通过返回的函数指针来调用它们,这样私有的成员函数也相当于变成了公有。(虽然返回指针指向某个成员函数的情况很少,但也需要注意)

回到刚才的例子上,我们刚才遇到的问题,只要对它们的返回类型加上 const 即可:

class Rectangle{
public:
    ...
    const Point& upperLeft() const {return pData->ulhc;}
    const Point& lowerRight() const {return pData->lrhc;}
    ... 
};

这样用户就只能读取矩形的 Point ,而不能修改它们。这意外着修饰函数的 const 也起了作用,因为它们不再允许用户修改。至于封装问题,我们让用户知道矩形的位置是完全合情合理的,所以我们给封装提供了有限的放宽,让用户可以读到私有数据,但不能修改。

即使这样,返回句柄仍然会带来问题——野句柄(dangling handles)。这种句柄所指东西(的所属对象)不复存在。这种“不复存在的对象”最常见的就是函数返回值。例如某个函数返回 GUI 对象的矩形外框:

class GUIObject{...}; //某个GUI对象 
const Rectangle boundingBox(const GUIObject& obj)); //以值传递方式返回一个矩形,条款3谈过为什么返回const

用户可能会这样使用代码:

GUIObject* pgo; 
... 
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());

boundingBox() 的调用会返回一个新的,临时Rectangle 对象,我们暂且称它为 temp。有了这个临时对象之后,我们会取得指向它左上角 Point 对象,然后 pUpperLeft 指向得了这个 Point 对象。可是 temp 毕竟是临时对象!在这行代码执行完后,temp会被销毁,它所包含的 Point 对象也就会被销毁,最后 pUpperLeft 指向一个不存在的对象的。也就是说,一旦 出了 pUpperLeft 那条语句,pUpperLeft就成野句柄。

这就是为什么函数返回指向内部成员"句柄"的总是危险的,不管你的"句柄"是指针,引用还是迭代器,不管你的返回值是不是 const,也不管那个返回句柄的成员函数是不是 const。这里唯一的关键是,有个句柄被传出去了,这时你就暴露在句柄比其所指对象更长寿的风险中。

但这不代表要杜绝这种做法,有时候必须得这样做,例如索引[]操作符,用来拿出容器(比如std::vector)里的个别元素,它返回的是指向容器里的数据的引用(见条款3),那些数据会随着容器被销毁而销毁。尽管如此,这样的函数毕竟是例外,不是常态。

Note:

  • 避免返回指向内部成员的"句柄"(包括指针,引用,迭代器)。不返回"句柄"能增强封装性,让 const 函数真正 const,也能减少"野句柄"。

条款29:为“异常安全”而努力是值得的

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-12 19:24:25  更:2021-11-12 19:26:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:00:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码