C 裡類的前置宣告分析

2021-08-30 15:53:24 字數 3792 閱讀 2205

首先,讓我們來了解下c++裡函式的情況,然後再模擬到類的情況

我們在用c++寫**時(假設原始檔只有乙個main.cpp),經常會按照以下步驟寫:

先對自定義的函式進行宣告

在main函式裡呼叫第1步裡宣告好的函式

編寫函式的實際**,這一步也叫函式的定義

簡單例子如下,

#include

using namespace std;

void

func

(void);

// 函式宣告

intmain()

void

func

(void

)// 函式定義

可以看到c++允許函式的宣告和定義分開,並且只要函式宣告後就可以拿來使用,暫時不用去實現其具體定義,這其實也是可以對c/c++**進行模組化管理的基礎。

類似於函式的宣告和定義,c++裡類的宣告和定義也是可以分開的。我們可以先宣告而暫時不定義它,這種宣告就稱為類的前置宣告,forward declaration。

class screen;

這個前置宣告在**裡引入了名字screen,並指示screen是乙個類型別。

對於類型別screen來說,在它宣告之後定義之前是乙個不完全型別,所謂不完全型別就是我們知道screen是乙個類型別,但是我們不知道它到底包含了哪些成員。

不完全型別只能在非常有限的情況下使用:

只能定義指向這種不完全型別的指標或引用

只能宣告(但是不可以定義)以不完全型別作為引數或者返回型別的函式

以下**是乙個錯誤例子,在類link_screen裡不能使用screen去建立物件,只能去定義screen類的指標或引用

class screen;

// screen的前置宣告

class link_screen

;

以下**是正確例子,

class screen;

// screen的前置宣告

class link_screen

;

主要有2點:

節約編譯時間

我們平時在寫**時會使用#include來包含其他標頭檔案,然後呼叫這個標頭檔案提供的一些類,如果該標頭檔案裡包含了很多其他沒有被使用到的類,那麼編譯時會被一起編譯,這樣就會浪費一些不必要的時間,而使用前置宣告,編譯器就只編譯我們需要用到的類,這樣就會節約一點編譯時間

處理兩個類相互依賴的問題

假設有兩個類,叫a和b,如果a裡要用到b的成員函式,b裡要用到a的成員函式,如果直接按照如下這樣寫,就會出錯,

/******* a.h *******/

#ifndef _a_h_

#define _a_h_

#include

#include

"b.h"

class a};

#endif

// _a_h_

/*******************/

/******* b.h *******/

#ifndef _b_h_

#define _b_h_

#include

#include

"a.h"

class b};

#endif

// _b_h_

/*******************/

/***** main.h ******/

#include

"a.h"

intmain

(void

)/*******************/

這樣寫會導致無限迴圈包含,a.h包含b.h,b.h裡包含a.h,a.h裡又包含b.h,…,編譯就會出錯。

改用前置宣告,就會避免這樣的問題,不過寫法有一定的限制,只能定義指標或引用,而且不能通過指標或引用來呼叫類的方法,因為此時該類型別是不完全型別,還不知道裡面定義了哪些方法。

/******* a.h *******/

#ifndef _a_h_

#define _a_h_

#include

class b;

class a};

#endif

// _a_h_

/*******************/

/******* b.h *******/

#ifndef _b_h_

#define _b_h_

#include

class a;

class b};

#endif

// _b_h_

/*******************/

/***** main.h ******/

#include

"a.h"

#include

"b.h"

intmain

(void

)/*******************/

上述寫法是a中包含b的指標,b中包含a的指標,還可以寫成a中包含b的物件,b中包含a的指標,如下,

/******* a.h *******/

#ifndef _a_h_

#define _a_h_

#include

#include

"b.h"

class a};

#endif

// _a_h_

/*******************/

/******* b.h *******/

#ifndef _b_h_

#define _b_h_

#include

class a;

class b};

#endif

// _b_h_

/*******************/

/***** main.h ******/

#include

"a.h"

#include

"b.h"

intmain

(void

)/*******************/

這裡直接引用google c++ style裡的關於前置宣告的缺點說明:

(1) 前置宣告隱藏了依賴關係,標頭檔案改動時,使用者的**會跳過必要的重新編譯過程。

(2) 前置宣告可能會被庫的後續更改所破壞。前置宣告函式或模板有時會妨礙標頭檔案開發者變動其 api.例如擴大形參型別,加個自帶預設引數的模板形參等等。

(3) 前置宣告來自命名空間std:: 的 symbol 時,其行為未定義。

(4) 很難判斷什麼時候該用前置宣告,什麼時候該用 #include 。極端情況下,用前置宣告代替 includes 甚至都會暗暗地改變**的含義.

類的前置宣告既有優點又有缺點,我們使用時可以根據具體情況去選擇。這裡再引用下google c++ style裡的關於前置宣告的使用說明,

(1) 盡量避免前置宣告那些定義在其他專案中的實體.

(2) 函式:總是使用#include.

(3) 類模板:優先使用#include.

但是如果出現類的互相依賴,使用前置宣告還是乙個比較好的解決辦法,或者通過重新對類進行設計,來避免互相依賴。

C 類宣告 類前置宣告

參考自 關於前置型別宣告的注意點 一 class b class a class b 上述 能夠通過編譯。二 class b class a class b 上述 報錯。error list c2079 a adata uses undefined class b c2027 use of unde...

C 模板類的前置宣告

template class linkedstack template class node template class linkedstack linkedstack bool isempty const bool isfull const t top const linkedstack add...

C 類的前置宣告用法

問題 兩個類a b相互呼叫,在兩個類a和b的標頭檔案中 include 了所需的標頭檔案,編譯報錯。為什麼呢,a需要b,b需要a,形成了迴圈,違反了程式的確定性原則。原因在於 class bbb 這種方式僅僅是一種符號宣告,告訴編譯器存在bbb這個類,不會去確定bbb這個類的所佔資源 記憶體 大小和...