簡體   English   中英

在.h文件或.cpp文件中實現類之間的區別

[英]Difference between implementing a class inside a .h file or in a .cpp file

我想知道哪些是僅在頭文件中聲明和實現類之間的區別,與在標頭中對類進行類型化並在有效的.cpp文件中實現的常規方法相比。

為了更好地解釋我在說什么,我的意思是正常方法之間的區別:

// File class.h
class MyClass 
{
private:
  //attributes
  public:
  void method1(...);
  void method2(...);
  ...
};

//file class.cpp
#include "class.h"

void MyClass::method1(...) 
{
  //implementation
}

void MyClass::method2(...) 
{
  //implementation
}

和一個公正的方法:

// File class.h
class MyClass 
{
private:
  //attributes
public:
  void method1(...) 
  {
      //implementation
  }

  void method2(...) 
  {
    //implementation
  }

  ...
};

我可以得到主要區別:在第二種情況下,代碼包含在每個需要它生成相同實現的更多實例的其他文件中,因此是隱式冗余; 而在第一種情況下,代碼是自己編譯的,然后每個調用MyClass對象的調用都鏈接到class.cpp的實現。

但還有其他差異嗎? 取決於具體情況,使用方法而不是另一種方法更方便嗎? 我還讀過某個地方,直接將方法的主體定義到頭文件中是對編譯器內聯該方法的隱式請求,是真的嗎?

主要的實際區別在於,如果成員函數定義在標題的主體中,那么它們當然會為包含該標題的每個翻譯單元編譯一次。 當您的項目包含幾百或幾千個源文件,並且相關的類被廣泛使用時,這可能意味着大量的重復。 即使每個類僅由2個或3個其他類使用,標題中的代碼越多,工作量就越多。

如果成員函數定義在它們自己的轉換單元(.cpp文件)中,則它們被編譯一次,並且只有函數聲明被多次編譯。

確實,類定義中定義的成員函數(不僅僅是聲明的)是隱式inline inline並不意味着人們可能合理地猜測它意味着什么。 inline表示,函數的多個定義出現在不同的翻譯單元中,然后鏈接在一起是合法的。 如果類位於不同源文件將要使用的頭文件中,則必須執行此操作,因此該語言會嘗試提供幫助。

inline也是編譯器的一個暗示,該函數可以有用地內聯,但盡管名稱,這是可選的。 編譯器越復雜,就越能夠自己做出關於內聯的決定,並且對提示的需求就越少。 比實際的內聯標記更重要的是該函數是否完全可用於編譯器。 如果函數是在不同的轉換單元中定義的,那么在編譯對它的調用時它就不可用,因此如果有任何內容要內聯調用,則它必須是鏈接器,而不是編譯器。

通過考慮第三種可能的方法,您可能能夠更好地看到差異:

// File class.h
class MyClass
{
    private:
        //attributes
    public:
       void method1(...);
       void method2(...);
       ...
};

inline void MyClass::method1(...)
{
     //implementation
}

inline void MyClass::method2(...)
{
     //implementation
}

既然隱式內聯已經不在了,那么這個“all header”方法和“header plus source”方法之間仍然存在一些差異。 如何在翻譯單元之間划分代碼會對構建時發生的事情產生影響。

對包含實現的標頭的任何更改都將強制包含該標頭的所有其他類重新編譯和重新鏈接。

由於標頭的更改頻率低於實現,因此通過將實現放在單獨的文件中,可以節省大量的編譯時間。

正如其他一些答案已經指出的那樣,是的,在文件的class塊中定義方法將導致編譯器內聯。

是的,編譯器將嘗試內聯直接在頭文件中聲明的方法,如:

class A
{
 public:
   void method()
   {
   }
};

在分離頭文件中的實現時,我可以考慮以下方便:

  1. 由於相同的代碼包含在多個翻譯單元中,因此您不會遇到代碼膨脹
  2. 您的編譯時間將大大減少。 請記住,對於頭文件中的任何修改,編譯器必須構建直接或間接包含它的所有其他文件。 我想對於任何人來說再次構建整個二進制文件只是為了在頭文件中添加一個空格將是非常令人沮喪的。

是的,在類定義中定義方法相當於將它們inline聲明。 沒有其他區別。 定義頭文件中的所有內容沒有任何好處。

類似的東西通常在帶有模板類的C ++中看到,因為模板成員定義也必須包含在頭文件中(因為大多數編譯器不支持export )。 但是對於普通的非模板類,沒有必要這樣做,除非你真的想要將你的方法聲明為inline

對我來說,主要區別在於頭文件就像是類的“接口”,告訴該類的客戶端它的公共方法(它支持的操作)是什么,而客戶不擔心這些方法的具體實現。 從某種意義上說,它是一種從實現更改中封裝其客戶端的方法,因為只有cpp文件更改,因此編譯時間要少得多。

在過去,我創建了一個模塊,可以屏蔽各種CORBA發行版中的差異,並且可以在各種OS /編譯器/ CORBA庫組合上統一工作。 在頭文件中實現它使得使用簡單的include將其添加到項目更加容易。 同樣的技術保證了代碼在調用代碼時需要重新編譯的同時重新編譯,即使用不同的庫或不同的OS編譯時。

所以我的觀點是,如果你有一個相當小的庫,預計可以重復使用並在各種項目中重新編譯,使它成為一個標題提供了與其他項目集成的優勢,而不是將額外的文件添加到主項目或重新編譯外部庫/ obj文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM