詳解C 前置宣告

2022-09-25 06:54:10 字數 1423 閱讀 9074

前置宣告是c/c++開發中比較常用的技巧,主要用在三種情形:

前置宣告作用

根據其用途,前置宣告的主要作用為:

前兩種用途好理解,第三種稍微複雜點,但卻是前置宣告最重要的用途。其解決類a包含類b,同時類b包含類a的依賴問題。迴圈依賴一般是設計層面的問題,可通過介面、引入輔助類等手段化解。前置宣告也能解決,只是架構上稍微彆扭。

不管a和b是否定義在同乙個檔案中,c++永遠無法解決如下形式的迴圈依賴(後文解釋原因):

// file: a.hpp

#include "b.hpp"

class a ;

// file: b.hpp

#include "a.hpp"

class b ;

前置宣告解決該問題需要與指標配合,轉換成另一種形式。要點如下:

程式設計客棧

使用前置宣告後,以下是一種可行的解決形式(兩程式設計客棧個類均使用了前置宣告):

// file: a.hpp

//3. 移除對b的包含(使用了#pragma once或者#ifndef b_hpp等保護措施則無必要)

// 2. 前置宣告類b

class b;

class a ;

// file: b.hpp

// 3. 移除對a的包含(有包含保護則非必要)

// 2. 前置宣告類a

class b ;

深入前置宣告

如果你有其他程式語言的經驗,會發現c++有點怪異:j**a/c#等語言可以輕鬆做到迴圈引用,無需使用類似的前置宣告技巧。這不禁讓人思考:c++為何必須要用前置宣告才能化解?

原因在於c++定義物件有兩種方式:一種是a a形式,a即物件,呼叫成員變數或函式用.,物件在棧中分配;另一種是a* a,a是指標,呼叫成員變數或函式用->,其程式設計客棧指向位址儲存實際物件,物件在堆中分配。

分配物件需要知道具體的記憶體大小,但以下形式我們不能確定類a和類b物件的大小:

class a ;

class b ;

對於這個簡單例子,你可以直觀認為a和b占用同樣的記憶體,例如1位元組,但也可以是2位元組,3位元組等;根據記憶體對齊要求,一般是4位元組,8位元組等。無論哪種情況,編譯器無法確定其物件占用記憶體,便會報錯停止編譯。所以你應該知道為什麼c++永遠不應該(不能)這樣做了吧?

那為何前置宣告加指標的組合能解決迴圈引用問題的呢?因為正常情況下,資料型別指標在同一機器的編譯器裡佔同樣的記憶體。指標一般是4或者8個位元組,對應32和64位指標。用了指標,即使有迴圈引用,類的大小也能輕易的確定下來。這也是j**a/c#/python/php等可以輕鬆迴圈引用的原因:這些語言中,物件變數其實都是指標,也意味著物件變數都是引用傳遞。

如果不移除檔案的相互包含,能否省去前置宣告呢?答案是不能,原因如下:

總的來說,不管是否移除對方的標頭檔案,前置宣告都是必須的。實踐中為了避免檔案變動時重新編譯的耗費,移除不必要的標頭檔案是乙個好習慣。

C 前置宣告詳解及例項

c 前置宣告詳解及例項 一般的前置函式宣告 見過最多的前置函式宣告,基本格式 如下 include using namespace std void fun char ch,int pvalue,double dvalue void main void fun char ch,int pvalue,...

C 前置宣告

特點 被宣告的類不用重新編譯,節省編譯時間 比如a包含乙個指向b的指標,b包含a的乙個例項,這種情況下,使用前置宣告。易錯的點 class date class task1 因為分配器為d分配記憶體的時候,必須要知道 d的大小 主要應用場景是兩個標頭檔案相互包含的場景,建議僅將前置宣告用於解決迴圈引...

C 前置宣告

一般的前置函式宣告 見過最多的前置函式宣告,基本格式 如下 1 include 2 using namespace std 34 void fun char ch,int pvalue,double dvalue 56 void main 714 15void fun char ch,int pva...