c++模板函数文件组织,避免redefinition of 错误

  很渴望发现同样喜欢c、c++、linux编程的朋友,可以互相学习,交流经验,我自己创建了一个qq群,欢迎你的加入:284635371

首先说一下本贴要解决的问题:

1、模板函数的文件组织。

2、解决全局变量的重复定义错误。

3、对于命名空间的理解。



  1、最近由于工作需要,要把单位原来一前辈写的功能函数文件common.cpp,common.h重新编译重用,里面存在大量的模板函数。

他所使用的方法是在cpp源文件中定义了模板函数,在h头文件中声明了模板函数的原型,然后在主函数文件中cpp源文件和h头文件都include进来了,但是在我的编译环境中是编译出现重复定义错误。

我的编译环境为ubuntu linux,g++编译器。


  我的解决办法,我在cpp源文件中放普通函数定义,h头文件中放置普通函数函数原型和模板函数的定义,在主函数文件中include头文件common.h就好了。原因是如果定义模板函数,在使用处得可以看得到函数的定义,不能仅仅看到函数原型。可以参考c++ primer第四版16.3模板编译原型一节。



  2、重新编译,模板函数好了,出现了common.h文件中的全局变量char XX[n]重复定义分析错误的原因,单个文件编译是没错的,所以编译通过,应该是在程序编译结束连接时出错了,因为多个cpp文件包含h头文件时都把变量XX编译在内,在连接时有多个XX定义,所以出错。

  自己测试int a这样的全局变量在gcc编译能通过,g++不能通过;int a=10这样的全局变量定义gcc,g++全部出现重复定义错误。分析原因估计是int a第一次出现可以理解为定义,然后分配空间,第二次出现gcc编译器认为是声明,不会重复定义,所以不出错。然而,int a=10类似的定义是没有解释为声明的可能性的,所以都出错。

  解决办法:

  1、把全局变量定义放在cpp源文件中,在对应h头文件中extern声明一下,然后在用到时include头文件。

  2、第二种办法是借鉴uc/os中的实现,在别人帖子中学来的:

原文链接:http://hi.baidu.com/fukai5/blog/item/dbf102fd705836e7fd037ff2.html

为了大家方便,拷贝到此处。


最近在学习uC/OS操作系统,对其中定义的全局变量产生了好奇。作者将变量定义在头文件uCOS_II.H中,比如:
OS_EXT INT8U            OSIntNesting;            /* Interrupt nesting level                        */
OS_EXT INT8U            OSIntExitY;

OS_EXT INT8U            OSLockNesting;           /* Multitasking lock nestinglevel                */

OS_EXT INT8U            OSPrioCur;               /* Priority of current task                       */
OS_EXT INT8U            OSPrioHighRdy;           /* Priority of highest priority task */

我 们大家都知道C语言的全局变量的作用域是整个源程序,只能定义一次,如果其它文件要引用某个文件中的全局变量,那么可以用关键字extern在本文件中来 声明该变量,即可使用该变量。我们也知道变量的声明或是函数的声明是可以并且一般是放在该源文件所对应的头文件中的,那么对于定义可不可以呢?在实际的编程中我们很少这样去在一个头文件中定义一个全局变量,应为如果其它文件要使用这个变量的时候,就要用include来包含这个头文件,从而去引用这个变量,我们同样知道C编译器在原文件中编译到include这条语句的时候,是将对应的头文件内容拷贝到该处的,也就是说在头文件中定义的全局变量,其实还是定义到了该源文件中。那么如果另外一个源文件也想引用这个全局变量怎么办呢?是不是同理用include包含该头文件就可以了?如果你这样做了,编译器在编译的时候是不会报错的,因为你的程序语法没有任何错误,但是在连接(link...)的时候错误就来了。在VC++6.0环境中会报出:error LNK2001: unresolvedexternal symbol "int a" ()这样的错误。这是为什么呢?每一个源文件编译之后都会产生一个OBJ或O文件(我们叫它目标文件),之前说过源文件中的include 相当于将该头文件中的所有语句放在该处,全局变量也就相应的添加进来,问题就在这里,既然全局变量被相应包含进来,两个原文件中就都分别定义了该变量,但是前面也说过,全局变量的作用域是整个源程序,只能有定义一次,然而在这个源程序中该变量被定义了两次,当然要报错了。怎么解决呢?这里有两种解决办法可 供参考。

1、首先将该全局变量定义在源文件中,假设文件名为global.c,然后再创建一个头文件global.h,用extern关键 字声明该变量,当然文件要使用条件编译语句#ifndef  _XX_H_    #define _XX_H_  .......变量声明......   #endif 。其他文件如果要引用该变量时,直接用Include包含global.h即可。

2、这种方法思想来自于uc/os,它“违背”了这种一般原则,将全局变量定义在了头文件中。直接上源程序!

/* file 1-----------------------------------------------OS_uCOS_II.h */

#ifndef   OS_uCOS_II_H
#define   OS_uCOS_II_H

#ifdef   OS_GLOBALS
#define  OS_EXT
#else
#define  OS_EXT  extern
#endif

OS_EXT int g_Var;
#endif

/* file 2---------------------------------------------------------OS_uCOS_II.c*/

#define  OS_GLOBALS

#include "uCOS_II.h"

/*file3------------------------------------------------------------TEST.c*/

#include "uCOS_II.h"

void fun(int x)

{

       g_Var=x;

}

/* file4------------------------------------------------------MAIN.c*/

#include "uCOS_II.h"

void main(void)

{

       g_Var=10;

}

/* --------------------------------------------------end of program*/

这 种将全局变量定义在头文件中的方法,网上很多人调侃是钻空子,钻预编译命令的空子。赫赫,其实不管他是否钻空子,我认为这是一种不错的定义全局变量的方法,对于整个源程序变量的管理清晰直观。当然不是我这么认为,ucos作者这样做了,就有它的妙处。至于原理我大概介绍下:首先,我们可以看到全局变量 g_Var在文件TEST.C和MAIN.C中有引用,它的定义是在头文件OS_uCOS_II.h 中,但是在定义的该变量用到了宏OS_EXT,但是该宏是否被“赋值”取决于是否定义OS_GLOBALS宏,也就是说OS_EXT是否被关键字 extern替代是有条件的;然后,我们可以看到OS_GLOBALS宏在文件OS_uCOS_II.c中被定义,并且该文件包含了头文件 OS_uCOS_II.h,根据include替代原则,头文件OS_uCOS_II.h所有信息将被包含进来,OS_GLOBALS被首先定义,那么 OS_EXT就被定义为空,也即全局变量g_Var在该文件中被定义。同理可分析TEST.c文件和MAIN.c文件,由于这两个源文件只是包含头文件 OS_uCOS_II.h,而没有#define  OS_GLOBALS,因此OS_EXT将被关键字extern 替代,全局变量就只是作为声明出现。因此也就不会出现多次定义的连接错误。废话了这么多,其实就是条件编译语句的作用。

因此对于题目所提出的问题就有两种解释了。


  3、出现了全局变量的重复定义错误后,自己思考了一下,程序中全局变量的作用域的情况,想出了c++命名空间的必要性。

  在c、c++程序中,在文件作用域声明的变量在整个工程中都是可见的,不管是在cpp源文件中还是在h头文件,因为最后都有一个要把所有的中间文件.o链接的过程,所有在文件外的名字在工程中都是flat的。在大的工程中这是一个非常恐怖的事,因为我在小工程中命名已经非常蛋疼了。所以出现了对命名空间的需求,不知道理解的对不对呀,不对的话希望大牛们指正。

  还有一个这次中收获的东西,其实用到别人的动态库的时候,只要知道其中函数原型,不需要h头文件也可以调用其中的函数,可以用nm命名查看。自己生成动态库后测试了一下,哈哈

  很渴望发现同样喜欢c、c++、linux编程的朋友,可以互相学习,交流经验,我自己创建了一个qq群,欢迎你的加入:284635371

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值