前置聲明相關

前置聲明相關

一個前置聲明是指在程序員尚未給出完整定義之前對一個標示符(一個類型、一個變量或者一個函數)的聲明。一個很簡單的例子就是我們在函數A中使用了函數B,但是函數B的聲明在函數A之后,這個時候,就需要對函數B進行前置聲明,實際上就是在函數A之前提供一個函數B的原型(prototype)。這種現象其實在C語言編程中我們已經習以為常了,在C++中亦是如此,只不過在編寫較大規模程序的時候,由于定義了較多的類,而這些類之間有可能是互相依賴的,換言之,類與類之間會互相引用,包括對成員函數的引用等等。

比如需要在文檔類調用視圖類

在doc頭文件加前置聲明
class CTestView;
如有必要在doc源文件加上testview.cpp(或者加在頭文件也可),
在testview也是如此,在頭文件
加前置聲明
class CTestDoc;
源文件加testdoc.cpp(或者加在頭文件也可)

一、類嵌套的疑問

C++頭文件重復包含實在是一個令人頭痛的問題,前一段時間在做一個簡單的數據結構演示程序的時候,不只一次的遇到這種問題。假設我們有兩個類A和B,分別定義在各自的有文件A.h和B.h中,但是在A中要用到B,B中也要用到A,但是這樣的寫法當然是錯誤的:

復制到剪貼板??C/C++代碼
[cpp]?view plaincopy
  1. class?B;?????
  2. ????
  3. class?A?????
  4. {?????
  5. ???public:?????
  6. ???B?b;?????
  7. };?????
  8. ????
  9. class?B?????
  10. {?????
  11. ???public:?????
  12. ???A?a;?????
  13. };?????
  14. ????

因為在A對象中要開辟一塊屬于B的空間,而B中又有A的空間,是一個邏輯錯誤,無法實現的。在這里我們只需要把其中的一個A類中的B類型成員改成指針形式就可以避免這個無限延伸的怪圈了。為什么要更改A而不是B?因為就算你在B中做了類似的動作,也仍然會編譯錯誤,表面上這僅僅上一個先后順序的問題。
為什么會這樣呢?因為C++編譯器自上而下編譯源文件的時候,對每一個數據的定義,總是需要知道定義的數據的類型的大小。在預先聲明語句class B;之后,編譯器已經知道B是一個類,但是其中的數據卻是未知的,因此B類型的大小也不知道。這樣就造成了編譯失敗,VC++6.0下會得到如下編譯錯誤:
error C2079: ‘b’ uses undefined class ‘B’
將A中的b更改為B指針類型之后,由于在特定的平臺上,指針所占的空間是一定的(在Win32平臺上是4字節),這樣可以通過編譯。

二、不同頭文件中的類的嵌套

在實際編程中,不同的類一般是放在不同的相互獨立的頭文件中的,這樣兩個類在相互引用時又會有不一樣的問題。重復編譯是問題出現的根本原因。為了保證頭文件僅被編譯一次,在C++中常用的辦法是使用條件編譯命令。在頭文件中我們常常會看到以下語句段(以VC++6.0自動生成的頭文件為例):

#if !defined(AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_)
#define AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_
//很多語句……
#endif

其中首句#if !defined也經常做#ifndef,作用相同。意思是如果沒有定義過這個宏,那么就定義它,然后執行直到#endif的所有語句。如果下次在與要這段代碼,由于已經定義了那個宏,因此重復的代碼不會被再次執行。這實在是一個巧妙而高效的辦法。在高版本的VC++上,還可以使用這個命令來代替以上的所有:
#pragma once
它的意思是,本文件內的代碼只被使用一次。

但是不要以為使用了這種機制就全部搞定了,比如在以下的代碼中:

C/C++代碼
[cpp]?view plaincopy
  1. //文件A.h中的代碼?????
  2. #pragma?once?????
  3. ????
  4. #include?“B.h”?????
  5. ????
  6. class?A?????
  7. {?????
  8. ???public:?????
  9. ???B*?b;?????
  10. };?????
  11. ????
C/C++代碼
[cpp]?view plaincopy
  1. //文件B.h中的代碼?????
  2. #pragma?once?????
  3. ????
  4. #include?“A.h”?????
  5. ????
  6. class?B?????
  7. {?????
  8. ???public:?????
  9. ???A*?a;?????
  10. };?????
  11. ????


?

這里兩者都使用了指針成員,因此嵌套本身不會有什么問題,在主函數前面使用#include “A.h”之后,主要編譯錯誤如下:
error C2501: ‘A’ : missing storage-class or type specifiers
仍然是類型不能找到的錯誤。其實這里仍然需要前置聲明。分別添加前置聲明之后,可以成功編譯了。代碼形式如下:

C/C++代碼
復制到剪貼板??C/C++代碼
[cpp]?view plaincopy
  1. //文件A.h中的代碼?????
  2. #pragma?once?????
  3. ????
  4. #include?“B.h”?????
  5. ????
  6. class?B;?????
  7. ????
  8. class?A?????
  9. {?????
  10. ???public:?????
  11. ???B*?b;?????
  12. };?????
  13. ????
[cpp]?view plaincopy
  1. //文件B.h中的代碼?????
  2. #pragma?once?????
  3. ????
  4. #include?“A.h”?????
  5. ????
  6. class?B;?????
  7. ????
  8. class?B?????
  9. {?????
  10. ???public:?????
  11. ???A*?a;?????
  12. };?????
  13. ????

這樣至少可以說明,頭文件包含代替不了前置聲明。有的時候只能依靠前置聲明來解決問題。我們還要思考一下,有了前置聲明的時候頭文件包含還是必要的嗎?我們嘗試去掉A.h和B.h中的#include行,發現沒有出現新的錯誤。那么究竟什么時候需要前置聲明,什么時候需要頭文件包含呢?

三、兩點原則

頭文件包含其實是一想很煩瑣的工作,不但我們看著累,編譯器編譯的時候也很累,再加上頭文件中常常出現的宏定義。感覺各種宏定義的展開是非常耗時間的,遠不如自定義函數來得速度。我僅就不同頭文件、源文件間的句則結構問題提出兩點原則,僅供參考:

第一個原則應該是,如果可以不包含頭文件,那就不要包含了。這時候前置聲明可以解決問題。如果使用的僅僅是一個類的指針,沒有使用這個類的具體對象(非指針),也沒有訪問到類的具體成員,那么前置聲明就可以了。因為指針這一數據類型的大小是特定的,編譯器可以獲知。

?

第二個原則應該是,盡量在CPP文件中包含頭文件,而非在頭文件中。假設類A的一個成員是是一個指向類B的指針,在類A的頭文件中使用了類B的前置聲明并便宜成功,那么在A的實現中我們需要訪問B的具體成員,因此需要包含頭文件,那么我們應該在類A的實現部分(CPP文件)包含類B的頭文件而非聲明部分(H文件)。

posted on 2013-07-21 16:05 zhanzc 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/jameszhan/p/declare.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274153.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274153.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274153.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

陌生人社會_陌生人之旅

陌生人社會The Last of Us Part II is a game that is deeply invested in the minutiae of its characters. The pain they cause, the things that drive them, and the particularities of their self-destruction and salvation. The game’s commitment to the true natur…

設計師更高效_要成為更好的設計師,我們需要更多的時間進行游戲

設計師更高效重點 (Top highlight)I’m a busy designer. I’m fortunate to be booked out months in advance. My freelance career has proven more stable than other’s “jobs”. I don’t wear busy as a badge of honour. I don’t condone hustle culture or compare …

java數據類型及其說明

Java數據類型及其說明java基本數據類型: java數據類型分為基本數據類型和引用數據類型,基本數據類型就是4類8種,分為數值類型(整數型[byte,short,int,long]),字符型&…

ux設計師薪水_公司與 設計機構:UX設計師的津貼和陷阱

ux設計師薪水Written by Yegor Tsynkevich由Yegor Tsynkevich撰寫 The more companies understand the power of a great user experience design and its impact on customer loyalty, the more they are willing to have it embedded in their culture. With so much emphas…

ZOJ 2165 Red and Black

1.采用dfs&#xff1a; #include <iostream>#include <cstdio>#include <cstring>#include <string>using namespace std;char map[25][25];int count 1;int r,c;int dx[4] {1,-1,0,0};int dy[4] {0,0,1,-1};bool judge(int x,int y){ if(x<0…

java中的equals用法

在Object 類中定義有&#xff1a; 1、public boolean equals(Object object )方法提供定義對象是否“相等”邏輯。 2、Object的equals方法定義為&#xff1a;x.equals(y)當x和y是同一個對象的引用時&#xff0c;返回true&#xff0c;否則返回false 3、在其他一些類中&#xff0…

根據圖片獲得配色方案_配色系列(1)—從圖片中獲得配色靈感

根據圖片獲得配色方案前言 (Foreword) When we start designing mobile web pages, we always need to determine the color scheme of the web page first. Well, at this time, unless the customer proposes a color scheme, most of the situations we have to face is to …

七大查找算法

1. 順序查找2. 二分查找3. 插值查找4. 斐波那契查找5. 樹表查找6. 分塊查找7. 哈希查找 查找是在大量的信息中尋找一個特定的信息元素&#xff0c;在計算機應用中&#xff0c;查找是常用的基本運算&#xff0c;例如編譯程序中符號表的查找。本文簡單概括性的介紹了常見的七種查…

HA2795Billboard 可用線段樹

#include<iostream>using namespace std;#include<cstdio>#include<algorithm>#define maxn 200005int h,n,w;int root[maxn<<4];int ans;//標記void make_tree(int l,int r,int rt){if(lr){root[rt]w;return ;}int mid(rl)/2;make_tree(l,mid,rt*2);m…

axure下拉列表框單選框_如何在Axure中創建下拉菜單和組合框

axure下拉列表框單選框First, let’s clarify what exactly is a dropdown menu, and what is a combo box, aren’t they the same? Well … no, not really, let me explain.首先&#xff0c;讓我們弄清楚什么是下拉菜單&#xff0c;什么是組合框&#xff0c;不是嗎&#xf…

Android 第一課 Activity

以下說明基于Android Studio&#xff0c;簡稱AS。&#xff08;紅色字體為自行添加&#xff0c;注在醒目&#xff09; 具體包括&#xff1a;創建活動創建項目 加載布局 在AndroidManifest文件中注冊 活動中使用&#xff08;提醒&#xff09;Toast 在活動使用&#xff08;菜…

figma設計_一種在Figma中跟蹤設計迭代的簡單方法

figma設計As designers, telling a good story is always part of the job. A great story engages the client with the journey of decision making; it shows your team the breadth and depth of the exploration; it also encourages us to reflect on our own design pro…

Android 第二課 Intent

上一節&#xff0c;掌握了活動的創建&#xff0c;但是在啟動器中點擊應用的圖標只會進入到該應用的主活動&#xff0c;那么&#xff0c;如何從主活動跳轉到其他活動呢&#xff1f;顯式IntentIntent有多個構造函數&#xff0c;其中一個是Intent(Context packContext,Class<?…

ok~加油!

你有夢想&#xff0c;放手去做&#xff01;轉載于:https://www.cnblogs.com/the-one/p/3217124.html

latex 插圖 上下放_專輯插圖中上下文中的文本

latex 插圖 上下放Especially if, like me, you’re not properly educated in the world of visual design, typography, and all those other things that a formal education can bring. We’re kind of playing around until something fits right, and doesn’t feel jarr…

亡羊補牢,為時不晚?

亡羊補牢&#xff0c;為時不晚 人總是想著第二次比第一次做的好&#xff0c;可是會一直有第二次機會嗎&#xff1f;當你思考好&#xff0c;決定做一件事情的時候&#xff0c;為什么不堅持下去呢&#xff1f;當你思考好&#xff0c;記住堅持到底就會勝利。祝福自己&#xff0…

2013年7月份第4周51Aspx源碼發布詳情

大型企業通用管理ERP源碼 2013-7-26 [VS2010]2013.7.4更新內容&#xff1a;1.修復決策模式-客戶等級不能保存問題。2.修復企業知識庫有報錯問題。3.修復運營模式-人力資源分析模塊-在部分模塊點擊查詢后&#xff0c;水晶報表顯示無法加載文件問題&#xff0c;4.修復行政辦公模…

視覺感知_產品設計中的視覺感知

視覺感知The role of the UX designer has evolved immensely over time, but at its core, it remains the same- UX設計人員的角色隨著時間的流逝而發生了巨大的變化&#xff0c;但從本質上講&#xff0c;它保持不變- to deliver information to users in an effective mann…

Android 第三課 Activity的生命周期

新建項目ActivityLifeCycleTest&#xff0c;創建主活動后&#xff0c;再新建兩個子活動--NormalActivity和DialogActivity。 現在活動及其對應布局文件創建完畢。 編輯normal_layout.xml文件&#xff0c;代碼如下&#xff1a; <?xml version"1.0" encoding"…

轉載:Apache commons開源工具簡介

Apache Commons是一個非常有用的工具包&#xff0c;解決各種實際的通用問題&#xff0c;下面是一個簡述表&#xff0c;詳細信息訪問http://jakarta.apache.org/commons/index.html BeanUtilsCommons-BeanUtils 提供對 Java 反射和自省API的包裝 BetwixtBetwixt提供將 JavaBean …