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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章