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(第四章)的更多相关文章

  1. C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)

    4.4 指向Member Function的指针 (Pointer-to-Member Functions) 取一个nonstatic data member的地址,得到的结果是该member在 cl ...

  2. 05 技术内幕 T-SQL 查询读书笔记(第四章)

    第四章 子查询:在外部查询内嵌套的内部查询(按照期望值的数量分为,标量子查询 scalar subqueries,多值子查询multivalued subqueries)(按照子查询对外部查询的依赖性 ...

  3. 第四章 JavaScript操作DOM对象

    第四章   JavaScript操作DOM对象 一.DOM操作 DOM是Document Object Model的缩写,即文档对象模型,是基于文档编程的一套API接口,1988年,W3C发布了第一级 ...

  4. HTML第十四章总结 HTML forms

    第十四章主要讲了 html forms,通过 forms,我们可以得到 customers' feedback,使得网页能够 interactive,本章的内容分为三个部分: forms 的 elem ...

  5. 第四章、前端之BOM和DOM

    目录 第四章.前端之BOM和DOM 一.解释BOM和DOM 二.window对象 三.window子对象 四.弹出框 五.计时相关 六.HTML的DOM树 七.查找元素 八.节点操作 九.JS操作CS ...

  6. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...

  7. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化 学习目标 对Direct 3D编程在 ...

  8. 【C++】《Effective C++》第四章

    第四章 设计与声明 条款18:让接口容易被正确使用,不易被误用 请记住 好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达到这些性质. "促进正确使用"的办法包括 ...

  9. 【odoo14】第十四章、CMS网站开发

    第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...

随机推荐

  1. vue -- 7 个 有用的 Vue 开发技巧

    1 状态共享 随着组件的细化,就会遇到多组件状态共享的情况, Vuex当然可以解决这类问题,不过就像 Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,今天我们介绍的是 vu ...

  2. sicily 1004. 简单哈希

    Description 使用线性探测法(Linear Probing)可以解决哈希中的冲突问题,其基本思想是:设哈希函数为h(key) = d, 并且假定哈希的存储结构是循环数组, 则当冲突发生时,  ...

  3. Kali linux 2016.2(Rolling)中metasploit的端口扫描

    目前常见的端口扫描技术一般有如下几类: TCP  Connect.TCP SYN.TCP ACK.TCP FIN. Metasploit中的端口扫描器 Metasploit的辅助模块中提供了几款实用的 ...

  4. CUDA笔记(11)

    CUDA提供了一种cudaEvent_t的类型,这种类型Event可以统计GPU上面某一个任务或者代码段的精确运行时间 使用常量内存的光线跟踪器的性能比使用全局内存的性能提升了50% __consta ...

  5. vue中计算小数保留两位小数

    代码

  6. Mojo Associated Interfaces

    Mojo Associated Interfaces yzshen@chromium.org 02/22/2017 Background Before associated interfaces ar ...

  7. 利用第三方类 phpmailer 发邮件

    第一.百度一下 phpmailer 随便找个 girhub 网站 download 下来即可. 第二.复制如下代码放在项目根目录,填写完整你的账号信息,即可发送邮件.就是这么简单! <?php ...

  8. django 在非空的字段里插入现象表述

    1.char 类型设置为非空 对于字段不赋值 默认储存为''(空字符串) 2.int 类型设置为非空 对于字段不赋值 单条插入 报错        多条数据同时插入 默认设置为0

  9. Oracle11g密码忘记处理方法

    c:\>sqlplus /nolog sql>connect / as sysdba sql>alter user 用户名 identified by 密码;(注意在这里输入的密码是 ...

  10. 恐怖的奴隶主(bob)

    题目 试题3:恐怖的奴隶主(bob) 源代码:bob.cpp 输入文件:bob.in 输出文件:bob.out 时间限制:1s 空间限制:512MB 题目描述 小L热衷于undercards. 在un ...