本貼為**
這篇文章很大程度是受到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的定義:
1a繼承至c
2a有乙個型別為c的成員變數
3a有乙個型別為c的指標的成員變數
4a有乙個型別為c的引用的成員變數
5a有乙個型別為std::list的成員變數
6a有乙個函式,它的簽名中引數和返回值都是型別c
7a有乙個函式,它的簽名中引數和返回值都是型別c,它呼叫了c的某個函式,**在標頭檔案中
8a有乙個函式,它的簽名中引數和返回值都是型別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
#include
#include
#include
//不同名字空間的前置宣告方式
namespace test1
...namespace test2
...;
private:
std::list_list;
std::vector_vector;
std::map_map;
c* _pc;
c& _rc;};}
c.h#ifndef c_h
#define c_h
#include
namespace test1
...{
class c
...{
public:
void print() ...{std::cout<<"class c"<#endif // c_h
C 的前置宣告
今天一開始看一篇幾年前翻譯的google風格規範,知道了前置宣告 可以顯著減少需要包含的標頭檔案數量 因此搜尋讀了一些文章,最後才發現現在的google風格規範是不推薦使用的。這一篇大概寫了3個小時,然後在剪貼簿被我覆蓋了,重寫花了1個多小時。前置宣告 forward declaration 是類 ...
前置宣告(Forward declaration)
前置宣告是指宣告乙個類或結構體而不定義它,比如 class a struct b 在宣告之後,定義之前,該類或結構體被稱為不完全型別 incompletion type 意思是,知道型別a和b,但不知道他包含哪些內容 不完全型別的使用有一些限制,不如不能用它來定義物件,但可以定義指象該型別物件的指標...
C 前置宣告
特點 被宣告的類不用重新編譯,節省編譯時間 比如a包含乙個指向b的指標,b包含a的乙個例項,這種情況下,使用前置宣告。易錯的點 class date class task1 因為分配器為d分配記憶體的時候,必須要知道 d的大小 主要應用場景是兩個標頭檔案相互包含的場景,建議僅將前置宣告用於解決迴圈引...