C++对象模型——Inline Functions(第四章)
4.5 Inline Functions
以下是Point class 的一个加法运算符的可能实现内容:
class Point {
friend Point operator+(const Point&, const Point&);
};
Point operator+(const Point &lhs, const Point &rhs) {
Point new_pt;
new_pt._x = lhs._x + rhs._x;
new_pt._y = lhs._y + rhs._y;
return new_pt;
}
理论上,一个比較"干净"的做法是使用 inline 函数set和get函数来完毕.
// void Point::x(float new_x) { _x = new_x; }
// float Point::x() { return _x; }
new_pt.x(lhs.x() + rhs.x());
因为受限仅仅能在上述两个函数中对_x直接存取,因此也就将稍后可能发生的data members的改变(比如在继承体系中上移或下移)所带来的冲击最小化了.假设把这些存取函数声明为 inline,就能够继续保持直接存取members的那种高效率--同一时候我们也兼顾了函数的封装性.此外,加法运算符不再须要被声明为Point的一个
friend.
然而,实际上并不能强迫将不论什么函数都变成 inline.关键词 inline(或 class declaration中的member function或 friend function的定义)仅仅是一项请求.假设这项请求被接受,编译器就必须觉得它能够用表达式合理地将这个函数扩展开来.
编译器相信它能够合理地扩展一个 inline 函数,意思是在某个层次上,其运行成本比一般的函数调用以及返回机制所带来的负荷低.cfront有一套复杂的測试法,通经常使用来计算assignments,function calls,virtual function calls等操作的次数.每一个表达式种类有一个权值,而 inline 函数的复杂度就以这些操作的总和来决定.
一般而言,处理一个 inline 函数,有两个阶段:
1.分析函数定义,以决定函数的"intrinsic inline ability"(本质的 inline 能力)."intrinsic"(本质的,固有的)一词在这里意指"与编译器相关".
假设函数因其复杂度或建构问题,被推断为不可成为 inline,它会被转为一个 static 函数,并在"被编译模块"内产生相应的函数定义.
2.真正的 inline 函数扩展操作是在调用的那一点,这会带来參数的求值操作以及暂时性对象的管理.
相同是在扩展点上,编译器将决定这个调用是否"不可为inline".在cfront中,inline 函数假设仅仅有一个表达式,则其第二或后继的调用操作:
new_pt.x(lhs.x() + rhs.x());
就不会被扩展开来,这是由于在cfront中它被变成:
new_pt.x = lhs._x + x_5PointFv(&rhs);
这就全然没有带来效率上的改善!对此,唯一可以做的就是重写其内容:
new_pt.x(lhs._x + rhs._x);
形式參数 (Formal Arguments)
在 inline 扩展期间,究竟真正发生了什么事情?是的,每个形式參数都会被相应的实际參数代替.假设说有什么副作用,那就是不能够仅仅是简单地一一封塞程序中出现的每个形式參数,由于这将导致对于实际參数的多次求值操作.一般而言,面对"会带来副作用的实际參数",通常都须要引入暂时性对象.换句话说,假设实际參数是一个常量表达式,能够在替换之前先完毕求值操作;后继的
inline 替换,就能够把常量直接"绑"上去.假设既不是常量表达式,也不是带有副作用的表达式,那么就直接替换它.
举个样例,如果有下面简单的 inline 函数:
inline int min(int i, int j) {
return i < j ? i : j;
}
以下是三个调用操作:
inline int bar() {
int minval;
int val1 = 1024;
int val2 = 2048;
/*1*/ minval = min(val1, val2);
/*2*/ minval = min(102, 2048);
/*3*/ minval = min(foo(), bar()+1);
return minval;
}
标示为1的那一行会被扩展为:
// 參数直接替换
minval = val1 < val2 ? val1 : val2;
标示为2的那一行会被扩张为:
// 替换后,直接使用常量
minval = 1024;
标示为3的那一行则引发參数的副作用,它须要导入一个暂时对象,以避免反复求值:
// 有副作用,所以导入暂时对象
int t1;
int t2;
minval = (t1 = foo()), (t2 = bar() + 1), t1 < t2 ? t1 : t2;
局部变量 (Local Variables)
假设轻微地改变定义,在 inline 定义中增加一个局部变量,会如何:
inline int min(int i, int j) {
int minval = i < j ? i : j;
return minval;
}
这个局部变量须要什么额外的支持或处理吗?假设有下面的调用操作:
{
int local_var;
int minval;
// ...
minval = min(va1, val2);
}
inline 被扩展后,为了维护其局部变量,可能会变成这样子(理论上这个样例中的局部变量能够被优化,其值能够直接在minval中计算):
{
int local_val;
int minval;
// 将inline函数的局部变量处以"mangling"操作
int __min_lv_minval;
minval = (__min_lv_minval = val1 < val2 ? val1 : val2), __min_lv_minval;
}
一般而言,inline 函数中的每个局部变量都必须放在函数调用的一个封闭区段中,拥有一个独一无二的名称.假设 inline 函数以单一表达式扩展多次,那么每次扩展都须要自己的一组局部变量.假设 inline 函数以分离的多个式子被扩展多次,那么仅仅需一组局部变量,就能够反复使用.
inline 函数中的局部变量,再加上有副作用的參数,可能会导致大量暂时性对象的产生.特别是假设它以单一表达式被扩展多次的话.比如,以下的调用操作:
minval = min(val1, val2) + min(foo(), foo() + 1);
可能被扩展为:
// 为局部变量产生暂时变量
int __min_lv_minval_00;
int __min_lv_minval_01;
// 为放置副作用值而产生暂时变量
int t1;
int t2;
minval = ((__min_lv_minval_00 = val1 < val2 ? val1 : val2), __min_lv_minval_00) +
((__min_lv_minval_01 = (t1 = foo()), (t2 = foo() + 1), t1 < t2 ? t1 : t2), __min_lv_minval_01);
inline 函数对于封装提供了一种必须的支持,可能有效存取封装于 class 中的nonpublic数据.它同一时候也是C程序中大量使用的 #define (前置处理宏)的一个安全替代品--特别是假设宏中的參数有副作用的话,然而一个
inline 函数假设被调用太多次的话,会产生大量的扩张码,使程序的大小暴涨.
參数带有副作用,或是以一个单一表达式做多重调用,或是在 inline 函数中有多个局部变量,都会产生暂时性对象,编译器或许可以把它们移除.此外,inline 中再有 inline,可能会使一个表面上看起来平庸的 inline 却因其连锁复杂度而没办法扩展开来.这样的情况可能发生于复杂度 class 体系下的constructors,或是object体系中一些表面上并不对的 inline 调用锁组成的串链--它们每个都会运行一小组运算,然后对还有一个对象发出请求.对于既要安全又要效率的程序,inline
函数提供了一个强而有力的工具.然而,与non-inline 函数比起来,它们须要更加小心地处理.
C++对象模型——Inline Functions(第四章)的更多相关文章
- C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)
4.4 指向Member Function的指针 (Pointer-to-Member Functions) 取一个nonstatic data member的地址,得到的结果是该member在 cl ...
- 05 技术内幕 T-SQL 查询读书笔记(第四章)
第四章 子查询:在外部查询内嵌套的内部查询(按照期望值的数量分为,标量子查询 scalar subqueries,多值子查询multivalued subqueries)(按照子查询对外部查询的依赖性 ...
- 第四章 JavaScript操作DOM对象
第四章 JavaScript操作DOM对象 一.DOM操作 DOM是Document Object Model的缩写,即文档对象模型,是基于文档编程的一套API接口,1988年,W3C发布了第一级 ...
- HTML第十四章总结 HTML forms
第十四章主要讲了 html forms,通过 forms,我们可以得到 customers' feedback,使得网页能够 interactive,本章的内容分为三个部分: forms 的 elem ...
- 第四章、前端之BOM和DOM
目录 第四章.前端之BOM和DOM 一.解释BOM和DOM 二.window对象 三.window子对象 四.弹出框 五.计时相关 六.HTML的DOM树 七.查找元素 八.节点操作 九.JS操作CS ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化 学习目标 对Direct 3D编程在 ...
- 【C++】《Effective C++》第四章
第四章 设计与声明 条款18:让接口容易被正确使用,不易被误用 请记住 好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达到这些性质. "促进正确使用"的办法包括 ...
- 【odoo14】第十四章、CMS网站开发
第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...
随机推荐
- 图论之堆优化的Prim
本题模板,最小生成树,洛谷P3366 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz 输入输出格式 输入格式: 第一行包含两个整数N.M,表示该图共有N个结点和M条无向边 ...
- XML与Plist文件转换
由于工作需要,要解析xml,举一个简单的例子,例如地址,如果是plist的话我们会很好的解析,但是如果已知一个xml的话,当然用原生的xml解析也能解析的出来,但是解析xml的话会是根据标签的头来解析 ...
- SQL流程控制语句
1 GoTo语句 IF 12>9GOTO print1ELSE GOTO print2 print1:PRINT '执行了流程1'--GOTO theEndprint2:PRINT '执行了流程 ...
- 关于 nginx 的配置的 location
精准匹配和普通匹配: server{ location =/index.htm{ ////精准匹 ...
- 数据库Flashback学习
最近更新时间:2018/12/18 适用场景 数据库升级.快速构建测试环境.DG中重建主库 前置条件 1. ARCHIVELOG 模式 数据库为 mount 状态下开启,最好指定archive log ...
- Windows 10 计划带来颜文字和Sandbox
在最新的 Window 10 测试版 Build 18305 中,Windows 10 增加了对颜文字(kaomoji)的支持. Kaomoji 是由日本符号序列组成的面脸部表情的名称.虽然有些人,比 ...
- dstat---统计磁盘,CPU,IO,等相关信息
dstat命令是一个用来替换vmstat.iostat.netstat.nfsstat和ifstat这些命令的工具,是一个全能系统信息统计工具.与sysstat相比,dstat拥有一个彩色的界面,在手 ...
- 05-数据类型转换(bool类型)
- C#+ArcGIS Engine 获取地图中选中的要素
转自 C#+ArcGIS Engine 获取地图中选中的要素 C#+ArcGIS Engine 获取地图中选中的要素 提供一种简单遍历获取地图中选中要素的方法,代码如下: List<IFeatu ...
- [Python] Pandas load DataFrames
Create an empty Data frame with date index: import pandas as pd def test_run(): start_date='2017-11- ...