日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

由friend用法引出的声明与定义那些事儿

發布時間:2025/3/15 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 由friend用法引出的声明与定义那些事儿 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天遇到了一個問題,大致描述一下就是有兩個類A和B。我想達到如下效果:B是A的友元,同時A是B的類類型成員。

第一次嘗試,在B.h中包含A.h,在A.h中包含B.h,在A類中聲明friend class B,在B類的定義中加入A a;

這一次嘗試必然失敗,編譯報錯,缺少分號什么的,原因是相互包含頭文件。google了一下,找到一篇文章,解釋的很好。

=================================華麗的引用線============================

一、類嵌套的疑問

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

class B;class A {public:B b; };class B {public:A a; };

?

因為在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
它的意思是,本文件內的代碼只被使用一次。

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

1 //文件A.h中的代碼 2 #pragma once 3 4 #include "B.h" 5 6 class A 7 { 8 public: 9 B* b; 10 }; 11 12 //文件B.h中的代碼 13 #pragma once 14 15 #include "A.h" 16 17 class B 18 { 19 public: 20 A* a; 21 };

?

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

//文件A.h中的代碼 #pragma once#include "B.h"class B;class A {public:B* b; };//文件B.h中的代碼 #pragma once#include "A.h"class B;class B {public:A* a; };

?

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

三、兩點原則

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


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


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

=================================華麗的引用線=================================

之后找了幾個網友的評論,很有借鑒意義

“兩個類相互引用,不管哪個類在前面,都會出現有一個類未定義的情況。而類的聲明就是提前告訴編譯器,所要引用的是個類,但此時后面的那個類還沒有定義,因此無法給對象分配確定的內存空間,因此只能使用類指針?!?/p>

“如果光是指針,可以前置聲明
如果互相引用實體,那一定是錯誤的設計,需求本身就不正確?!?/p>

“如果在A的頭文件中你不需要知道B的細節,那么壓根不需要包含B的頭文件。”

“按照C++規范

1 任何類可以聲明多次
2 多次聲明如果存在沖突, 那么不同編譯器的處理方式可能不同,不過從開發者角度有義務保證多次聲明不沖突。
3 任何類都必須先聲明再引用,但是不必先定義。”

這里需要額外指出的是,前向聲明的類在定義之前是一個不完全類型,不能定義該類型的對象,不完全類型只能用于定義指向該類型的指針及引用,或者用于聲明使用該類型作為形參類型或返回類型的函數。

所以,如果要在類B中訪問類A的私有變量,那么可以將類A作為類B成員函數的參數(指針或者引用)傳遞給B,設a為A的實例,利用a.x或a->x來訪問A中私有成員變量,類似的,私有成員函數通過a.x()和a->x()訪問。(不可用雙冒號作用域符訪問,必須用對象,除非是靜態方法)

friend聲明其實類似于前向聲明,在此基礎上多了訪問權限。下面是我從stackoverflow上找的問答,可以很好解釋這個問題。

=================================華麗的引用線=================================

Q:

I was wondering if you have to #include "Class1.h" in a class that is using that as a friend. For example the .h file for the class that is granting permission to Class1 class.

class Class2 {
friend class Class1;
}
would you need to #include "Class1.h" or is it not necessary? Also in the Class2 class, Class1 objects are never created or used. Class1 just manipulates Class2 never the other way around.

A:

The syntax is:

friend class Class1;
And no, you don't include the header.

More generally, you don't need to include the header unless you are actually making use of the class definition in some way (e.g. you use an instance of the class and the compiler needs to know what's in it). If you're just referring to the class by name, e.g. you only have a pointer to an instance of the class and you're passing it around, then the compiler doesn't need to see the class definition - it suffices to tell it about the class by declaring it:

class Class1;
This is important for two reasons: the minor one is that it allows you to define types which refer to each other (but you shouldn't!); the major one is that it allows you to reduce the physical coupling in your code base, which can help reduce compile times.

To answer Gary's comment, observe that this compiles and links fine:

class X;

class Y
{
X *x;
};

int main()
{
Y y;
return 0;
}
There is no need to provide the definition of X unless you actually use something from X.

=================================華麗的引用線=========================================

這里還有一篇討論friend,前向聲明與相關命名空間的問題,之后再抽時間研究,這里先mark一下。

http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces

轉載于:https://www.cnblogs.com/ShaneZhang/p/3511439.html

總結

以上是生活随笔為你收集整理的由friend用法引出的声明与定义那些事儿的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。