文档章节

使用内联函数的一个问题

Peter87
 Peter87
发布于 2015/10/14 22:41
字数 1187
阅读 112
收藏 2

最近碰到一个与内联方法有关的编译问题,记叙如下。

问题背景

类Scheduler的实现如下所示,其中方法SetStates()仅仅被类本身使用(暂且先不管它的public属性)。

// scheduler.hpp
class Scheduler {

public:
  Scheduler():m_state1(0),m_state2(0) {}
  ~Scheduler() {}

  inline void SetStates(int state1, int state2);

  int GetState1() {
    return m_state1;
  }
  int GetState2() {
    return m_state2;
  }

private:
  int m_state1;
  int m_state2;
};
// scheduler.cpp
void Scheduler::SetStates(int state1, int state2) {
  m_state1 = state1;
  m_state2 = state2;
}

如上代码构建正常。之后,新建一个新的类SchedulerMgmt,并且在其中使用了类Scheduler当中的SetStates()方法:

// SchedulerMgmt.cpp
void SchedulerMgmt::UpdateSchedulerState(int state1, int state2) {
  m_scheduler.SetStates(state1, state2);
}

重新编译,提示错误:

scheduler.hpp:10: warning: inline function ‘void Scheduler::SetStates(int, int)’ used but never defined
/tmp/ccsOhsNk.o: In function `SchedulerMgmt::UpdateSchedulerState(int, int)':
scheduler-mgmt.cpp:(.text+0x111): undefined reference to `Scheduler::SetStates(int, int)'
collect2: ld returned 1 exit status

由于编译错误的产生仅仅是在新增加类SchedulerMgmt,并在其中调用了Scheduler中的SetStates()之后才产生,同时该方法恰好声明为内联方法,很自然的便怀疑到这个方法的声明之上:inline。于是先后尝试了下面两种方法来排错:

  • A: 在scheduler.cpp当中为SetScheduleParams方法的实现加上"inline"关键字。
  • B: 去掉scheduler.hpp当中SetScheduleParams方法的“inline”声明。

测试的结果是第一种方法A不管用,编译错误依然存在,方法B解决了问题。

这样的结果让自己感到疑惑不已。众所周知,将方法声明为内联的方式有两种,其一为在class的定义当中定义成员方法;其二是使用inline关键字。第一种方式经过测试是可行的,然而为什么第二种方式并没有达到目的呢?难道问题出在外部调用内联方法上面?这是第一个疑惑。

方法B在将SetStates()的内联属性去掉问题消失,这是不难理解的。但当我仅仅在其定义上加上inline修饰符,便又会出现问题,只是这个时候仅仅剩下链接的问题:

/tmp/ccoizmZA.o: In function `SchedulerMgmt::UpdateSchedulerState(int, int)':
scheduler-mgmt.cpp:(.text+0x111): undefined reference to `Scheduler::SetStates(int, int)'
collect2: ld returned 1 exit status

这是另外一个疑惑。看来对于内联的许多细节,自己之前并未注意到。

内联方法的基本要求

在查阅了C++标准最新的Draft之后,从中找到了针对“方法A没有解决编译错误”的解释:

An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). [ Note: A call to the inline function may be encountered before its definition appears in the translation unit. —end note ].

这里提到内联函数的基本要求:任一调用该函数的地方均需要看到它的定义(这一点上,C++中的template也有这样的要求)。所以在每一个编译单元都需要定义(注:在程序的构建工程当中,编译阶段会对每一个源代码文件分别编译、汇编,之后将之后的输出文件进行链接等处理,这里的编译单元可以看做是一个个独立的源代码文件。)可见,将内联成员方法定义在类的定义里面是最为稳妥的。在方法A当中,类SchedulerMgmt中尽管可以看到SetStates()声明为inline,但是却无法见着它的定义,便提示错误。这即是如上第一个疑惑的解答案。

编译器对于内联方法处理上的差异

对于第二个疑惑,其实可以归结于一点:编译阶段对于类中的普通成员方法与内联成员方法的处理有差异,但差异在什么地方?要回答这个问题,可以从编译之后的结果上去看。

使用objdump -s -d scheduler.o分别查看输出修改前后的scheduler.cpp编译之后的汇编码:

可见,在将SetStates()定义为内联方法时,在编译之后的目标文件中并不会包含该函数的指令,也就是说编译器将一个方法内联之后,并不会在目标文件当中将其保留,就如同宏在预处理阶段直接文本扩展一样。因此,到这里第二个疑惑解开。

上面是笔记的主要内容。其实在参考资料<sup>2</sup>链接的一篇文章中介绍了有关extern inline的知识,权因本笔记是对工作中问题的一次小总结,所以不再将其纳入讨论。

编译器:

g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) Copyright (C) 2006 Free Software Foundation, Inc.

参考:
  1. << Working Draft, Standard C++, n4296.pdf >>
  2. http://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html
  3. << 程序员的自我修养—链接、装载与库 >>

© 著作权归作者所有

共有 人打赏支持
Peter87
粉丝 0
博文 11
码字总数 21993
作品 0
杭州
程序员
私信 提问
C语言内联函数

内联函数也称内嵌函数,它主要解决程序的运行效率。 #####################问题######################################### 函数调用需要建立栈内存环境,进行参数传递,并产生程序执行转移,...

晨曦之光
2012/03/09
741
0
C++ inline

今天学习C++的内联函数(inline),写了个测试代码如下: 程序运行时查看汇编代码: 执行过程和调用普通函数并没有什么区别,后来查看了一些资料,说是inline只是在编译阶段给出一个建议,是否...

gnal
2015/09/12
227
6
Kotlin基础之内联函数

内联函数 使用高阶函数会给运行时带来一些坏处:每个函数都是一个对象,捕获闭包(如:访问函数体内的变量),内存分配(函数对象或Class),虚拟调用引入的运行过载。 使用内联Lambda表达式...

code_xzh
2018/01/10
0
0
const成员函数,内联函数,友元函数,static成员

1.const成员函数 2.内联函数 3.友元 4.static成员 5.N中构造拷贝构造的优化 1.const成员函数 const修饰成员函数 在成员函数后面加const,const修饰this指针所指向的对象,也就是保证调用这个...

qq_38646470
2018/04/02
0
0
Kotlin:关于内联函数的一些理解

inline函数(内联函数)从概念上讲是编译器使用函数实现的真实代码来替换每一次的函数调用,带来的最直接的好处就是节省了函数调用的开销,而缺点就是增加了所生成字节码的尺寸。基于此,在代...

泪已无痕
2018/05/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mysql 查询当天、本周,本月,上一个月的数据

今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 昨天 SELECT * FROM 表名 WHERE TO_DAYS( NOW( ) ) - TO_DAYS( 时间字段名) <= 1 近7天 SELECT * FROM 表名 wher......

BraveLN
今天
2
0
Android Multimedia框架总结(六)C++中MediaPlayer的C/S架构

前面几节中,都是通过java层调用到jni中,jni向下到c++层并未介绍 看下Java层一个方法在c++层 MediaPlayer后续过程 frameworks/av/media/libmedia/MediaPlayer.cpp 找一个我们之前熟悉的setDa...

天王盖地虎626
今天
3
0
【Linux】【MySQL】CentOS7安装最新版MySQL8.0.13(最新版MySQL从安装到运行)

1、前言   框框博客在线报时:2018-11-07 19:31:06   当前MySQL最新版本:8.0.13 (听说比5.7快2倍)   官方之前表示:MySQL 8.0 正式版 8.0.11 已发布,MySQL 8 要比 MySQL 5.7 快 2 ...

Code辉
今天
5
0
oracle dg备库重建redolog:ora-00313,ora-00312

trace文件: Errors in file /crbank/dbs/app/product/diag/rdbms/rdbs/dbs/trace/dbs_mrp0_24445130.trc: ORA-00313: open failed for members of log group 8 of thread 1 ORA-00312: onl......

hnairdb
今天
1
0
深入分析Java I/O的工作机制 (一)

1.Java的I/O类库的基本架构 先说一下什么是类库:可以说是类的集合,类库包括接口、抽象类、具体类等。 I/O是机器获取和交互信息的主要渠道。 java在I/O上也一直在做持续的优化,在1.4版开始...

java菜分享
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部