为什么头文件中不要写函数定义
2014-03-22 22:46
253 查看
昨天看了一下预编译头文件的问题,领会到了预编译头文件的强大用处。于是乎,手痒尝试之。编写一甚为简单的小程序。程序的大概框架如下:
基本代码内容和文件的关系就是这样,先编译了一下, 没问题!编译通过!接着链接,然而在链接过程中出现了如下问题:
error LNK2005:"void __cdecl print_out(void)" (?print_out@@YAXXZ) already defined inpreCompeiled.obj
fatal errorLNK1169: one or more multiply defined symbols found
很明显这是个链接错误,而且是一个链接时期的重复定义问题。从这里我们能看到在头文件中最好还是不要定义函数,只做函数声明。若真要定义的话,在头文件所定义的函数前加上inline 使之成为内联函数即可。
分析一下其中原因,为什么在编译期可以通过而在链接期却发现问题。
由于两个cpp文件所使用的函数或者变量,都在其所包含的文件当中,编译器在检查编译依赖时,完全可以找到相关的定义,由于void print_out()函数是在头文件中定义的,编译器独立编译两个cpp文件是完全可以通过的,生成两个obj文件,并且这个头文件被编译两次,同一个函数,void print_out()都独立被编译进两个obj文件中。然而在链接期间,链接器发现两个obj中都有void print_out()。虽然这两个实质就是同一个头文件中的定义,但是链接器不知道编译器是怎么编译的,它不承认你俩是同一个东西,并放你过去。他只会觉得混乱,不知道在链接之后,到底应该调用哪个print_out()。这就相当于,两个不同的头文件中都有函数名,参数,返回值等完全相同的函数,哪怕是在其对应的cpp文件中定义的。那么在一个主cpp文件中包含这两个不同的头文件,并使用函数名调用这个函数,那么链接时出现的情况同上,也是重复定义。此时链接器罢工了,他就报一个重复定义的错误。
那么为什么我们加上inline,使头文件中的函数成为内联函数就可以正常编译链接运行呢了?
我们知道,内联函数在编译期间,会将所有调用该内联函数的地方用内联函数完整的内容替替换,这种替换是一种文本式的替换。因此,在两份cpp文件中,由于打印函数所处的地方不一样,两个print_out()就是天然不同的,因此编译链接都是没有问题的。
通常我们的做法是不在头文件中定义函数或定义变量,都只是函数或变量的声明。
具体的定义由对应的cpp文件完成。那么为什么这样的方式就不会出现连接错误呢?
若以这样的方式写的话,对于print_out()的头文件和cpp文件可以独立编译,而其他需要调用该函数的文件则只需要包含其.h文件即可。其他文件编译时,不会对print_out()的cpp文件重新编译,并放进自己的obj中,而只是对其引用而已。因此在链接时,链接器知道print_out()的来源,自然链接过程中不会出现以上的问题了。
/*preCompeiledTest.cpp:*/ #include "print_out.h" int main() { print_out(); } /*print_out.h:*/ #include <stdio.h> inline voidprint_out() { printf("Hello world"); } /*stdafx.h:*/ #if !defined(AFX_STDAFX_H__A91FAFC6_F875_48E0_AB6F_561191F0ED77__INCLUDED_) #defineAFX_STDAFX_H__A91FAFC6_F875_48E0_AB6F_561191F0ED77__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windowsheaders #include <stdio.h> #include "print_out.h" /*stdafx.cpp:*/ #include"stdafx.h"
基本代码内容和文件的关系就是这样,先编译了一下, 没问题!编译通过!接着链接,然而在链接过程中出现了如下问题:
error LNK2005:"void __cdecl print_out(void)" (?print_out@@YAXXZ) already defined inpreCompeiled.obj
fatal errorLNK1169: one or more multiply defined symbols found
很明显这是个链接错误,而且是一个链接时期的重复定义问题。从这里我们能看到在头文件中最好还是不要定义函数,只做函数声明。若真要定义的话,在头文件所定义的函数前加上inline 使之成为内联函数即可。
分析一下其中原因,为什么在编译期可以通过而在链接期却发现问题。
由于两个cpp文件所使用的函数或者变量,都在其所包含的文件当中,编译器在检查编译依赖时,完全可以找到相关的定义,由于void print_out()函数是在头文件中定义的,编译器独立编译两个cpp文件是完全可以通过的,生成两个obj文件,并且这个头文件被编译两次,同一个函数,void print_out()都独立被编译进两个obj文件中。然而在链接期间,链接器发现两个obj中都有void print_out()。虽然这两个实质就是同一个头文件中的定义,但是链接器不知道编译器是怎么编译的,它不承认你俩是同一个东西,并放你过去。他只会觉得混乱,不知道在链接之后,到底应该调用哪个print_out()。这就相当于,两个不同的头文件中都有函数名,参数,返回值等完全相同的函数,哪怕是在其对应的cpp文件中定义的。那么在一个主cpp文件中包含这两个不同的头文件,并使用函数名调用这个函数,那么链接时出现的情况同上,也是重复定义。此时链接器罢工了,他就报一个重复定义的错误。
那么为什么我们加上inline,使头文件中的函数成为内联函数就可以正常编译链接运行呢了?
我们知道,内联函数在编译期间,会将所有调用该内联函数的地方用内联函数完整的内容替替换,这种替换是一种文本式的替换。因此,在两份cpp文件中,由于打印函数所处的地方不一样,两个print_out()就是天然不同的,因此编译链接都是没有问题的。
通常我们的做法是不在头文件中定义函数或定义变量,都只是函数或变量的声明。
具体的定义由对应的cpp文件完成。那么为什么这样的方式就不会出现连接错误呢?
若以这样的方式写的话,对于print_out()的头文件和cpp文件可以独立编译,而其他需要调用该函数的文件则只需要包含其.h文件即可。其他文件编译时,不会对print_out()的cpp文件重新编译,并放进自己的obj中,而只是对其引用而已。因此在链接时,链接器知道print_out()的来源,自然链接过程中不会出现以上的问题了。
相关文章推荐
- 为什么不要在头文件中写上函数定义?
- 头文件中不要去定义函数与变量
- 模板类成员函数的定义和声明为什么要放在一个文件中
- c++模板类/模板函数的声明与定义应该放在头文件里,不要分开来写类中函数的声明与定义
- 看到这个你能明白很多为什么要定义new,为什么函数不要定义
- 【Skynet】C头文件不要定义函数?
- 为什么C语言的同一个文件中可以定义两个接口完全相同的函数?
- 为什么inline函数应该在头文件中定义?
- C++中inline函数的定义为什么要放在头文件中?头文件中“只定义一份”是什么意思?const object 和inline函数是“一次定义规则”
- 头文件中不要去定义函数
- 学习笔记之C++为什么将函数声明或者类的定义放在.h文件中,而将其实现放在原文件中
- 模板类成员函数的定义和声明为什么要放在一个文件中
- 模板类成员函数的定义和声明为什么要放在一个文件中
- C++:为什么inline函数的定义要放在头文件里 + inline和宏定义的区别(暂未体会到)
- const对象为什么可以在头文件中定义
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- xsl 文件如何定义 Javascript 函数并且调用
- 为什么在C++使用pthread_create()的时候,类成员函数做线程的处理函数必须要定义成static类型的?
- 为什么要把父类的析构函数定义成虚函数?
- 永远不要在.h文件中定义变量----->转载一篇博客