vb 根据pid获取句柄_C++中避免返回指向对象内部的句柄(handles)
点蓝色字关注“CurryCoder的程序人生”
微信公众号:CurryCoder的程序人生
欢迎关注我,一起学习,一起进步!
1.问题的引入
假如你正在给一个应用写一个矩形类,这个矩形由左上角和右下角的顶点坐标表示。为了表示这两个点,我们写一个表示点的类:
class Point{public: Point(int x, int y); void setX(int newVal); void setY(int newVal); // ....};
为了让矩形对象的体积小一点,我们将这两个顶点装在另一个结构体中,并用指针指向它:
struct RectData{ Point ulhc; // 左上角 Point lrhc; // 右下角};
class Rectangle{ // ...private: std::shared_ptr pData; };
由于用户想要得到点的坐标,所以需要让矩形类要提供返回这两个点的函数。因为矩形类是我们自定义的类,根据之前的文章C++中多用引用传递方式替换值传递方式中提到的对于自定义的类,传递引用方式比传值方式更高效,所以我们让这两个函数返回引用:
class Rectangle{public: // ... Point& upperLeft() const { return pData->ulhc; }
Point& lowerRight() const { return pData->lrhc; }private: std::shared_ptr pData; };
上面的代码虽然可以通过编译,但却是自我矛盾的!首先,函数upperLeft()和函数lowerRight()被声明为const成员函数。因为它们的目的是为了只返回一个对象而别的什么都不做,但两个函数却都返回了指向私有成员的引用,因此调用者就能通过这个引用来改变对象。
Point coord1(0, 0);Point coord2(100, 100);const Rectangle rec(coord1, coord2); // 我们希望它是常对象recrec.upperLeft().setX(50); // upperLeft()的调用者rec能够使用被返回的指向rec内部的Point成员变量的引用来更改成员// 但是,rec实际上是不可变的,因为它是常对象
2.得到的结论从上面的代码段中,我们可以得到下面两个教训:(1).数据成员的最好封装性取决于最能破坏封装的函数。虽然ulhc和lrhc两个点都声明为private,但由于存在两个返回引用的函数的存在,它们其实相当于是public。因为public函数upperLeft()和lowerLeft()传出了它们的引用。(2).如果一个函数返回了指向储存在对象外部的数据成员的引用,即使这个函数声明为了const,这个函数的调用者也能修改这个成员。原因见之前的文章尽可能使用const修饰符中的bitwise constness的局限性。除了引用,返回指针和迭代器也是相同的结果,也是由于相同的原因导致。引用、指针、迭代器都是本文标题中所说的"句柄"(handle),即接触对象的某种方式。直接返回句柄总会带来破坏封装的风险,这也导致声明为const的函数并不是真正的const。注意:"内部成员"除了内部数据还包括内部函数,即声明为私有(private)或保护(protected)的函数。因此,对于内部函数也是一样,也不要返回它们的句柄,否则用户也可以通过返回的函数指针来调用它们,这样私有的成员函数也相当于变成了公有。
3.问题的解决方法
回到上面出现自我矛盾的代码段,如果要解决返回引用会导致数据成员被改变的问题,只需要给函数的返回类型加上一个const。如下面的代码段所示:
class Rectangle{public: // ...
// 现在返回的是const Point& const Point& upperLeft(){ return pData->ulhc; }
const Point& lowerRight(){ return pData->lrhc; }private: std::shared_ptr pData; };
这样用户就只能对其进行读操作而不能进行写操作了,给函数声明的const也就不会骗人了。至于封装性问题,让用户知道这个矩形的位置是完全合情合理的,所以我们给封装提供了有限的放宽,让用户可以读到私有数据,但坚决不能让用户执行写操作。然而即使这样,返回的句柄仍然会导致一个问题:"野句柄"(dangling handle),即这个句柄指向的对象不存在。最常见的场景是函数返回值,假如我们正在给某个GUI对象写一个返回它边界框的函数,返回类型是Rectangle。如下面的代码段所示:
class GUIObject{ // ...};
const Rectangle boundingBox(const GUIObject& obj);
现在,客户可能会像下面那样使用这个函数:
GUIObject* pgo;// ...const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
现在有意思的事发生了,取址运算符括号里面的函数boundingBox()会返回一个新的临时Rectangle对象称为temp。有了这个临时对象之后,我们就可以获得指向它左上角的Point对象,然后pUpperLeft自然就获得了这个Point对象的地址。但是,temp毕竟是临时对象。在这行代码执行完后,temp会被销毁,它所包含的Point对象也会被销毁。最后,pUpperLeft存储了一个指向不存在的对象的指针。因此,这也解释了为什么返回指向内部成员"句柄"的函数是危险的,不管你的"句柄"是指针、引用还是迭代器;不管你的函数返回值是不是const、你的函数是不是const。但是,这不代表要杜绝这种做法,有时候不得不这样做。例如索引[]操作符,用来获取容器(比如std::vector)中的某个对象,它返回的是指向容器中的数据的引用,来让你完成写操作。记住,在我们自己设计的程序中还是尽量避免不要这么做。
4.总结
(1) 避免返回指向内部成员的"句柄"(包括指针,引用,迭代器)。不返回"句柄"能增强封装性,让const函数成为真正的const,也能减少"野句柄"。
觉得好看,请点这里↓↓↓
vb 根据pid获取句柄_C++中避免返回指向对象内部的句柄(handles)相关推荐
- 编程获取Revit视图中的所有实例对象数量
转载请复制以下信息: 原文链接: http://blog.csdn.net/joexiongjin/article/details/7678923 作者: 叶雄进, Autodesk ADN 问题: ...
- ssh备考-06Struts2 往值栈中存入值,以及前端获取值栈中的值
目录 注意点: 一.先搭建好struts2框架 index.jsp struts.xml User.java 二.往值栈压值,前端如何获取值 1.获取值栈 ValueStack1Action.java ...
- 普通函数和箭头函数中的this指向
普通函数的this指向Window function a(){console.log(this) } a() //Window Dom元素绑定事件时的this,句柄里的 this 值是该元素的引用 d ...
- $.ligerdialog.open中确定按钮加事件_彻底搞懂JavaScript中的this指向问题
JavaScript中的this是让很多开发者头疼的地方,而this关键字又是一个非常重要的语法点.毫不夸张地说,不理解它的含义,大部分开发任务都无法完成. 想要理解this,你可以先记住以下两点: ...
- java pid 获取句柄_获取进程pid、根据进程pid获取线程pid、获取线程进程句柄
======================================= 获取进程pid ======================================= #include &qu ...
- linux c 通过 pid 获取 进程相关信息 cmdline
linux系统命令ps和top都是通过读取/proc/$PID 目录下的信息获取进程的相关信息, 它遍历/proc目录下的首字符为数字的目录,获取系统进程的信息. 我们经常会把pid写到/var/ru ...
- linux c pid获取进程名 进程名获取pid
Liunx中 通过进程名查找进程PID可以通过 pidof [进程名] 来查找.反过来 ,相同通过PID查找进程名则没有相关命令. 在linux根目录中,有一个/proc的VFS(虚拟文件系统),系统 ...
- javamac系统通过pid获取进程名称_线上环境 Linux 系统调用追踪
提到如何动态追踪进程中的系统调用,相信大家第一时间都能想到 strace,它的基本用法非常简单,非常适合用来解决 "为什么这个软件无法在这台机器上运行?" 这类问题.但如果需要分析 ...
- 2021-03-31 Matlab simulink 模糊PID在无刷直流电机中的应用
Matlab simulink 模糊PID在无刷直流电机中的应用 无刷直流电机的数学模型. 1.无刷直流电机数学模型 然后给出无刷直流电机的动态数学模型 看到上述这张图,不知聪明的你有没有发觉,图中采 ...
最新文章
- c文本框只能输入数字_VBA代码限制文本框的输入
- 在Spring中使用JTA事务管理
- 2021年计算机三级新题型,2021年如何通过计算机三级考试的经验
- pack php 详解_函数pack的使用详解
- WCF学习- 基础概念
- freeradius的proxy功能
- sql ntext 替换存储过程
- Velox将在Pangolin上启动其算法交易机器人,并计划推出更多DeFi解决方案
- 程序员面试金典——4.3高度最小的BST
- Linux操作系统中netstat命令常见用法
- C++ 如何有效地使用对话框
- js--属性和方法(私有/公有)
- 二维热传导温度场有限元求解
- 读书-思考力|《金字塔原理》
- 计算机sci二区期刊,图像处理的sci二区期刊有哪些
- 【入门】倒序输出一个四位整数
- 2018艾耕科技笔试题
- 【pytest】三、pytest用例管理框架的前后置(固件、夹具)setup和teardown,及封装
- sql注入中的联合注入
- Android Fragmnet-Fragment数据交换以及ListFragment的使用
热门文章
- 静态html的ajax如何发请求,静态页面ajax - 冥焱的个人空间 - OSCHINA - 中文开源技术交流社区...
- Python 内置模块之 random
- mysql 5.7 延迟同步_MySQL5.6升级5.7时出现主从延迟问题排查过程
- 权限表使用联合主键吗_天天写 order by,你知道Mysql底层执行流程吗?
- Linux(Ubuntu 19.10)下 Qt5 连接 MySQL(QMYSQL driver not loaded)
- mysql的rows大小超过8126_mysql大字段(Row size too large8126)
- include包含头文件的语句中,双引号和尖括号的区别
- win10查看端口占用
- 2017年9月27日日志
- Angular之简单的登录注册