(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++第一周笔记

极客班GeekBand C++第一周笔记 防卫式声明 #ifndef _xxx_ #define _xxx_ (程序段) #endif 作用:不使用防卫式声明会反复include引入...
  • syx121328147
  • syx121328147
  • 2016年05月13日 18:10
  • 140

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

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

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

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

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

候老师在课程中提到,在编写代码的时候尽量使用inline函数。那么inline函数到底有没有效率上的提升呢?下面我们就来测试一下。 首先我们编写了两个函数,一个Add1(inline 函数),另一个...
  • illidanee
  • illidanee
  • 2016年05月09日 11:21
  • 614

(GeekBand或极客班)inline 函数到底写在哪?

平日里一直都用VS写代码。简单,方便。直到这次极客班作业。让我对 VS 产生了疑惑,同时产生了一个疑问 Inline 函数到写在哪里?什么样的方式才是正确的呢?为了验证,我查阅了很多资料。下面记录了我...
  • illidanee
  • illidanee
  • 2016年05月13日 16:10
  • 493

极客班 C++(上)第二周学习笔记

Geekband 第二周学习笔记 class without pointer class with pointer
  • s_tangbao
  • s_tangbao
  • 2016年07月28日 10:07
  • 248

Geekban极客班 C++第五周

vptr和vtbl 只要类里带有虚函数,对象里就会多一个指针。 继承函数是继承函数的调用权,父类有虚函数则子类一定虚函数。 虚指针vptr指向虚表vtbl,虚表中存放虚函数指针。 c++编译器...
  • syx121328147
  • syx121328147
  • 2016年06月11日 21:15
  • 249

(GeekBand或极客班)对象的生命周期小实验。

程序中有各种各样的,变量有的存在stack中,有的存在heap中。有Local变量,也有Global变量。那么,这么多变量的声明周期是怎样的呢?想想也是头大。不妨写个小程序测试下看看吧。 为了直观的...
  • illidanee
  • illidanee
  • 2016年05月14日 11:40
  • 172

极客班GeekBand - 互联网思维修炼 - 1

极客班GeekBand - 互联网思维修炼 - 1极客班GeekBand - 互联网思维修炼 - 1 讲师 - 陈世欣 名人看法 第一部分 产品探索 痛点思维 天使用户 天使用户可以加快对产品的探索 ...
  • lionpku
  • lionpku
  • 2015年07月27日 11:26
  • 1037

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

极客班GeekBand - C++面向对象高级开发 - 2极客班GeekBand - C面向对象高级开发 - 2 讲师 - 侯捷 复习Complex类的实现过程 三大函数拷贝构造拷贝复制析构 栈堆与内...
  • lionpku
  • lionpku
  • 2015年07月28日 16:12
  • 1224
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:(GeekBand或极客班) inline 函数的思考。(第二季)
举报原因:
原因补充:

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