[英]C++ class ordering
我開始使用C ++,來自C和Objective C(以及一些Java)。 我認為開始構建我的技能的好地方是從頭開始編寫一個簡單的哈希表,使用鏈接列表進行沖突。 所以我開始為每個班級編寫骨架。
class HashTable
{
public:
...
private:
...
};
class LinkedList
{
public:
...
private:
Node *root;
};
class Node
{
public:
Node *next;
string key;
int value;
Node()
{
...
}
};
關於這一點的奇怪之處在於,對於c ++用戶來說,這可能不會讓這個代碼無效。 我會得到一個錯誤:
error: expected type-specifier before ‘Node’
關於LinkedList類中的根節點。
當我簡單地重新排序類以便它是Node{...}; LinkedList{...}; HashTable{...};
Node{...}; LinkedList{...}; HashTable{...};
一切都像一個油潤的冰淇淋卡車。
現在,我不是一個質疑C ++設計的人,但這個限制有什么理由嗎? 如果我沒記錯的話,Obj。 C的課程基本上變成了桌子,並在飛行中查找。 那么這種行為的原因是什么?
編譯器拋出以下錯誤
error: expected type-specifier before ‘Node’
因為它(編譯器)還沒有(還)知道
Node *root;
Node
是什么。 (因為Node
稍后定義。)
兩種可能的解決方
在LinkedList
類之前放置Node
類的定義(你已經知道了)
通過放置此行,Forward在類LinkedList
之前聲明類Node
class Node;
這告訴編譯器存在一個類Node
。
在閱讀了PigBen的評論之后,您似乎在質疑這種行為的基本原理。 我不是編譯人員,但我認為這種行為使解析變得容易。 對我來說,它類似於在使用之前提供函數聲明。
PS:Nitpick,對於LinkedList
,變量名head
可能比root
更合適。
這種聲明的要求來自兩種力量。 首先,它簡化了編譯器設計。 由於類型和變量具有相同的標識符結構,因此只要編譯器解析標識符,編譯器就必須知道它遇到了什么。 有兩種方法可以做到這一點。 一種方法是要求在將其用於其他定義之前聲明每個標識符。 這意味着代碼必須在給出其定義之前轉發聲明它打算使用的任何名稱。 這是編寫具有其他模糊語法的編譯器的一種非常簡單的方法。
另一種方法是在多次傳遞中處理它。 每次遇到未聲明的標識符時,都會跳過它,編譯器會在解析整個文件后嘗試解析它。 事實證明,C ++的語法使得這很難正確完成。 編譯器編寫者不想不得不去解決這個問題,所以我們有前向聲明。
另一個原因是您實際上可能希望具有前向聲明,以便遞歸結構是確定性的,作為語言的內在屬性。 這有點微妙。 假設您編寫了一個相互遞歸的類網絡:
class Bar; // forward declaration
class Foo {
Bar myBar;
};
class Bar {
int occupySpace;
Foo myFoo;
};
這顯然是不可能的,因為occupySpace
成員將出現在無限嵌套的遞歸中。 要求定義中所有成員的前向聲明為此提供特定數量的信息。 特別是,它允許編譯器提供足夠的信息來形成對類的引用,但不允許實例化類(因為它的大小未知)。 前向聲明使這成為語言語法的一個特征,就像左值如何作為語言語法的特征而不是更微妙的語義或運行時要求一樣可分配。
這種行為的原因是歷史性的。 文件按順序處理。 在遇到標識符的第一個引用時,該標識符需要已經聲明。
編譯器不會首先處理整個文件。
您可以經常使用前向聲明,而不是重新排序類定義
class Node;
class List
{
public:
//...
private:
Node *root;
//...
};
//...
這種限制沒有技術上的原因 - 這可以通過編譯器在單個類的上下文中執行您明顯期望的事實來證明。 盡管如此,刪除“限制”確實會進一步使編譯器復雜化,減慢它們的速度,增加它們的內存使用量,而且至關重要的是 - 不會向后兼容(因為在更局部化的范圍(命名空間)中的匹配可能會被選中而不是之前看到的其他符號) 。
恕我直言,它也使代碼更難閱讀和理解。 能夠從上到下閱讀並隨時理解代碼非常有用,並鼓勵您更有思想和結構化地表達您的問題解決方案。
反過來考慮一下; 如果接受在文件中任何地方聲明的類是正確的,為什么不在另一個尚未遇到的文件中聲明的類呢?
如果你走得那么遠,那么在你嘗試鏈接程序之前你最終無法給出錯誤,這可能遠離問題實際發生的地方。
您已聲明您的LinkedList
類具有Node
類型,但編譯器不知道Node
是什么,因為它尚未聲明。
只需在LinkedList
之前聲明Node
這種風格意味着解析器可以更少次地運行代碼。 如果必須識別每個聲明的類型,然后再次運行代碼,則需要花費額外的時間來解析第二次運行文件。
當然,正如許多人所指出的那樣,你可以使用前瞻性聲明。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.