使用前置宣告取代包含標頭檔案

2022-04-28 22:36:21 字數 2810 閱讀 6328

**:

c++ 關於宣告,定義,類的定義,標頭檔案作用,防止標頭檔案在同一編譯單元中重複引用,不具名空間:

這篇文章很大程度是受到exceptional c++ (hurb99)書中第四章 compiler  firewalls and the pimpl idiom  (編譯器防火牆和pimpl慣用法) 的啟發,這一章講述了減少編譯時依賴的意義和一些慣用法,其實最為常用又無任何***的是使用前置宣告來取代包括標頭檔案。

item 26 的guideline - "never #include a header when a forward declaration will suffice"

在這裡,我自己總結了可以使用前置宣告來取代包括標頭檔案的各種情況和給出一些示例**。

首先,我們為什麼要包括標頭檔案?問題的回答很簡單,通常是我們需要獲得某個型別的定義(definition)。那麼接下來的問題就是,在什麼情況下我們才需要型別的定義,在什麼情況下我們只需要宣告就足夠了?問題的回答是當我們需要知道這個型別的大小或者需要知道它的函式簽名的時候,我們就需要獲得它的定義。

假設我們有型別a和型別c,在哪些情況下在a需要c的定義:

a繼承至c

a有乙個型別為c的成員變數

a有乙個型別為c的指標的成員變數

a有乙個型別為c的引用的成員變數

a有乙個型別為std::list的成員變數

a有乙個函式,它的簽名中引數和返回值都是型別c

a有乙個函式,它的簽名中引數和返回值都是型別c,它呼叫了c的某個函式,**在標頭檔案中

a有乙個函式,它的簽名中引數和返回值都是型別c(包括型別c本身,c的引用型別和c的指標型別),並且它會呼叫另外乙個使用c的函式,**直接寫在a的標頭檔案中

c和a在同乙個名字空間裡面

c和a在不同的名字空間裡面

1,沒有任何辦法,必須要獲得c的定義,因為我們必須要知道c的成員變數,成員函式。

2,需要c的定義,因為我們要知道c的大小來確定a的大小,但是可以使用pimpl慣用法來改善這一點,詳情請

看hurb的exceptional c++。

3,4,不需要,前置宣告就可以了,其實3和4是一樣的,引用在物理上也是乙個指標,它的大小根據平台不同,可能是32位也可能是64位,反正我們不需要知道c的定義就可以確定這個成員變數的大小。

5,不需要,有可能老式的編譯器需要。標準庫裡面的容器像list, vector,map,

在包括乙個list,vector,map型別的成員變數的時候,都不需要c的定義。因為它們內部其實也是使用c的指標作為成員變數,它們的大小一開始就是固定的了,不會根據模版引數的不同而改變。

6,不需要,只要我們沒有使用到c。

7,需要,我們需要知道呼叫函式的簽名。

8,8的情況比較複雜,直接看**會比較清楚一些。

c

&dotoc(c&);

c&dotoc2(c

&c) ;

從上面的**來看,a的乙個成員函式dotoc2呼叫了另外乙個成員函式dotoc,但是無論是dotoc2,還是dotoc,它們的的引數和返回型別其實都是c的引用(換成指標,情況也一樣),引用的賦值跟指標的賦值都是一樣,無非就是整形的賦值,所以這裡即不需要知道c的大小也沒有呼叫c的任何函式,實際上這裡並不需要c的定義。

但是,我們隨便把其中乙個c&換成c,比如像下面的幾種示例:

1.c

&dotoc(c&);

c&dotoc2(c c) ;2.

c&dotoc(c);c&

dotoc2(c

&c) ;3.

c dotoc(c&);

c&dotoc2(c

&c) ;4.

c&dotoc(c&);

c dotoc2(c

&c) ;

無論哪一種,其實都隱式包含了乙個拷貝建構函式的呼叫,比如1中引數c由拷貝建構函式生成,3中dotoc的返回值是乙個由拷貝建構函式生成的匿名物件。因為我們呼叫了c的拷貝建構函式,所以以上無論那種情形都需要知道c的定義。

9和10都一樣,我們都不需要知道c的定義,只是10的情況下,前置宣告的語法會稍微複雜一些。

最後給出乙個完整的例子,我們可以看到在兩個不同名字空間的型別a和c,a是如何使用前置宣告來取代直接包括c的標頭檔案的:

a.h、

#pragma

once

#include

<

list

>

#include

<

vector

>

#include

<

map>

#include

<

utility

>

//不同名字空間的前置宣告方式

namespace

test1

namespace

test2

;private

:std::list

<

c>

_list;

std::vector

<

c>

_vector;

std::map

<

c, c

>

_map;c*

_pc;c&

_rc;};}

c.h

#ifndef c_h

#define

c_h#include

<

iostream

>

namespace

test1};}

#endif

//c_h

類前置宣告和標頭檔案包含

類的前置宣告 forward declaration 和包含標頭檔案 include 的區別常常會迷惑我們,特別是涉及兩個類相互包含的時候。因此我們有必要搞清楚二者的區別以及二者的適用場合。首先我們需要問乙個問題是 為什麼兩個類不能互相包含標頭檔案?所謂互相包含標頭檔案,我舉乙個例子 圖層類clay...

c 前置宣告與標頭檔案引用

優點 1.c 前置宣告,可以節約預處理器的展開時間,也就是在編譯的時候速度是增快了,但是伴隨著很多坑。2.當被前置宣告的類改動後,只需要編譯包含改動類標頭檔案的原始檔,所有使用了前置宣告的原始檔不需要改動 體現1.所有引用testb.h 的其他 cpp檔案不用再去包含 tem a 與 util 這倆...

對類前置宣告和包含標頭檔案的一點理解

類的前置宣告 forward declaration 和包含標頭檔案 include 的區別常常會迷惑我們,特別是涉及兩個類相互包含的時候。因此我們有必要搞清楚二者的區別以及二者的適用場合。首先我們需要問乙個問題是 為什麼兩個類不能互相包含標頭檔案?所謂互相包含標頭檔案,我舉乙個例子 我實現了兩個類...