(GeekBand或极客班) inline 函数的思考。(第二季)

原创 2016年05月30日 14:47:03

在第一季中,我简单的写了一个实验。来证明了inline函数的执行效率。


随后 Richard_HuTianyi 在评论中提出了一些自己的见解。

1--inline函数无法inline的情况。如果一个inline函数在头文件中被定义。但是编译器无法将其inline,将其看成普通函数处理,那么如果这个头文件在多个源文件中被包涵,是否会有函数重定义的情况呢?

2--如果不写inline编译器会不会去inline。


先来看下第一个问题:对此,在这里用一个小实验,观察一下结果。

首先在 Demo.h 中写一个复杂的 inline 函数。为了不让编译能够 inline 。代码如下:

Demo.h

#ifndef __DEMO_H__
#define __DEMO_H__

//循环加递归,想必不会inline
extern inline void Add(int level)
{	
	for (int i = 1; i < level; ++i)
	{
		Add(i);
	}
}

#endif

然后再 Demo1.cpp和Demo2.cpp中调用该函数。在这里如果是普通函数此时必定会产生重定义的错误。代码如下:

Demo1.h

#ifndef __DEMO1H__
#define __DEMO1_H__

#include "Demo.h"

extern void f1();

#endif

Demo1.cpp

#include "Demo.h"

void f1()
{
	Add(10);	
}

Demo2.h

#ifndef __DEMO2_H__
#define __DEMO2_H__

#include "Demo.h"

extern void f2();

#endif

Demo.cpp

#include "Demo.h"

void f2()
{
	Add(10);	
}

下面给出测试代码:

main.cpp

#include <iostream>

#include "Demo1.h"
#include "Demo2.h"

int main()
{

	f1();	

	f2();

	return 0;
}

编译运行。确实没有问题。但是这里有两种可能情况,

1. Add 函数被编译器 inline 。这种情况自然不会有问题。

2. Add函数没有被编译器 inline 。这种情况是我们要验证的。


如何确定上面执行的到底是哪种情况呢?这里我编译出了.s汇编文件。

打开Demo1.s和Demo2.s可以看到下面的代码:(代码很多,我只找到关键的地方。)

LVL26:
	.loc 1 10 0
	call	__Z3Addi

LBB73:
	.loc 1 10 0
	movl	%edx, (%esp)
	call	__Z3Addi

Ltext_cold0:
	.globl	__Z3Addi
	.def	__Z3Addi;	.scl	2;	.type	32;	.endef
__Z3Addi:

这里应该不难看出在Demo1.cpp和Demo2.cpp中发生了函数调用。而不是inline。至此。排除了上面的第一种情况,Add函数没有被编译器内联。




结论:如果inline函数不被编编译器inline,那么不会出现函数重定义的情况。


现在再来看看第二种情况:

我现在将 Demo.h 中的 Add 函数inline去掉,同时将函数代码简化,为了使编译器将其 inline 。代码如下:

#ifndef __DEMO_H__
#define __DEMO_H__

extern void Add(int level)
{	
	level += 1;
}

#endif

编译:

$ make
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  main.cpp -o main.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  main.i -o main.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  main.s -o main.o
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  Demo1.cpp -o Demo1.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  Demo1.i -o Demo1.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  Demo1.s -o Demo1.o
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  Demo2.cpp -o Demo2.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  Demo2.i -o Demo2.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  Demo2.s -o Demo2.o
g++ main.o Demo1.o Demo2.o -o debug
Demo1.o:在函数‘Z3Addi’中:
/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6: multiple definition of `Add(int)'
main.o:/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6:第一次在此定义
Demo2.o:在函数‘Z3Addi’中:
/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6: multiple definition of `Add(int)'
main.o:/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6:第一次在此定义
collect2: 错误:ld 返回 1
Makefile:32: recipe for target 'debug' failed
make: *** [debug] Error 1

果然:函数重定义了。那么将函数定义写入源文件试试呢。

修改代码如下:

Demo.h

#ifndef __DEMO_H__
#define __DEMO_H__


extern void Add(int level);

#endif
Demo.cpp

#include "Demo.h"

void Add(int level)
{	
	level += 1;
}

这次编译一下。嗯没问题。看看汇编文件.

	movl	$10, (%esp)
	call	__Z3Addi

看来还是函数调用。

结论:不加 inline 编译器不会将其 inline。



总结:
1--对于复杂的inline函数编译器不会将其inline。这种情况,不会出现重定义。
2--对于不加 inline 的函数。函数的实现不能写入头文件中,编译器不会自动将其inline,这种情况会出现函数重定义。写入源文件中的函数即使简单也不会 inline(如果inline 要在编译的时候找到函数的定义,写在源文件显然在其他文件引用这个函数的时候是看不到函数定义的。)。


版权声明:本文为博主原创文章,未经博主允许不得转载。

极客班GeekBand - C++面向对象高级开发 - 2

极客班GeekBand - C++面向对象高级开发 - 2极客班GeekBand - C面向对象高级开发 - 2 讲师 - 侯捷 复习Complex类的实现过程 三大函数拷贝构造拷贝复制析构 栈堆与内...
  • lionpku
  • lionpku
  • 2015年07月28日 16:12
  • 1106

极客班GeekBand C++第一周笔记

极客班GeekBand C++第一周笔记 防卫式声明 #ifndef _xxx_ #define _xxx_ (程序段) #endif 作用:不使用防卫式声明会反复include引入...

(GeekBand或极客班) 你的内存泄漏了--- 之 virtual 析构

在这次作业点评中发现很多同学都没有在基类的析构函数使用virtual。 这在类的继承中是非常危险的。因为你的析构没有正确的调用,你的内存可能在不断的泄漏。。。 下面用一个小实验测试一下吧。 ...

(GeekBand或极客班)简单对象构造过程分析。

在继承和组合中,对象的构造顺序一般是由编译器默认处理,我们在写代码的时候非常容易忽略,为了记录和验证本课中对象的构造和析构顺序,现在讲我的过程记录下来。 1--单一继承的情况: #incl...

极客班GeekBand - C++第一次课程辅导 - 李建忠

极客班GeekBand - C++第一次课程辅导 - 李建忠极客班GeekBand - C第一次课程辅导 - 李建忠 栈Stack VS 堆heap 堆对象的空间分析 栈对象的空间分析 变量模型与使用...
  • lionpku
  • lionpku
  • 2015年07月25日 19:27
  • 1851

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一)

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一)算法入门和编程风格
  • lionpku
  • lionpku
  • 2015年07月20日 16:23
  • 1206

小问题大思考之C++里的inline函数

inline,一个神奇的关键字。有了它,函数,你同时就可以获取函数和宏的优点。inline定义的函数,比起没有inline的函数来说,没有执行函数调用所带来的负担(对此可参见《C++程序的内存布局》)...

Geekban极客班 第三周

继承 复合

极客班C++(上)第一周课程笔记

防御式声明,const,private。

Geekban极客班 第二周

构造函数 inline String::String(const char* cstr) { if (cstr) { //判断指针所指是否为空 m_data = ne...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:(GeekBand或极客班) inline 函数的思考。(第二季)
举报原因:
原因补充:

(最多只允许输入30个字)