0

我最近在C++程序中遇到了一個我無法完全理解的行爲。讓我通過簡單的例子來解釋行爲。與編譯,靜態初始化和靜態庫有關的問題

一是靜態庫

在層次結構的最底層,我有一個靜態庫 - 讓它命名爲FirstLIB。這個庫包含兩對頭文件/源文件。 sample.h頭文件包含MyClass類定義。對應sample.cpp文件包含此類的實現(其方法)。該代碼介紹如下:

sample.h

#ifndef __sample_h 
#define __sample_h 

namespace SampleNamespace 
{ 
    class MyClass 
    { 
     int counter; 

    public: 
     MyClass(); 
     int GetCounter(); 
     void SetCounter(int value); 
    }; 
} 

#endif 

sample.cpp的

#include <iostream> 
#include "sample.h" 

namespace SampleNamespace 
{ 
    MyClass::MyClass(): counter(0) 
    { 
     std::cout << "Inside of MyClass constructor!" << std::endl; 
    } 

    int MyClass::GetCounter() { return counter; } 
    void MyClass::SetCounter(int value) { counter = value; } 
} 

起,dvcl.h文件聲明用於處理簡單的API MyClass對象和dvcl.cpp實現此API。需要注意的是,dvcl.cpp文件包含此API的方法使用的MyClass對象的定義。該變量被定義爲靜態的,所以它只能在這個源文件中可見。

dvcl.h

#ifndef _dvcl_h 
#define _dvcl_h 

void DVCL_Initialize(int counter); 
int DVCL_GetCounter(); 
void DVCL_SetCounter(int value); 

#endif 

dvcl.cpp

#include "dvcl.h" 
#include "sample.h" 

static SampleNamespace::MyClass myClass; 

void DVCL_Initialize(int counter) 
{ 
    myClass.SetCounter(counter); 
} 
int DVCL_GetCounter() 
{ 
    return myClass.GetCounter(); 
} 
void DVCL_SetCounter(int value) 
{ 
    myClass.SetCounter(value); 
} 

2.第二靜態庫

二靜態庫 - 讓它命名爲SecondLIB - 是甚至比第一個更簡單。它只包含一個頭/源對。 dvconference_client.h標頭聲明一個函數,而dvconference_client.cpp實現此功能。 dvconference_client.cpp還包含dvcl.h頭文件(這將觸發dvcl.cpp源文件的編譯)。該代碼可以發現如下:

dvconference_client.h

#ifndef __external_file 
#define __external_file 

int DoSomething(); 

#endif 

dvconference.cpp

#include "dvconference_client.h" 
#include "dvcl.h" 

int DoSomething() 
{ 
    return DVCL_GetCounter(); 
} 

3.主要可執行

最後,主可執行文件MainEXE只包含一個main.cpp文件。該源代碼包含dvconference_client.h和dvcl.h頭文件。該代碼介紹如下:

#include <iostream> 
#include "dvconference_client.h" 
#include "dvcl.h" 

int main() 
{ 
    std::cout << DoSomething() << std::endl; 
    std::cout << DVCL_GetCounter() << std::endl; 
    return 0; 
} 

4.我的疑惑和問題:

  • 如果我不叫引用myClass的對象(所以DoSomething的()或DVCL_功能之一的功能),MyClass構造函數不被調用。我期望myClass對象將在編譯dvcl.cpp時默認實例化。但是,看起來編譯器只在知道對象實際在運行時使用時纔會生成所需的語句。這是真的嗎?
  • 如果特定的頭文件(本例中爲dvcl.h)包含在不同的源中,則相應的dvcl.cpp僅被編譯一次。我記得有關這方面的東西,但我不確定這是否屬實。無論對應的頭文件包含多少,C++編譯器是否只編譯一次每個源文件實際上是正確的。
  • 在dvcl.cpp中定義的myClass對象僅實例化一次。如果我正確理解第二點,並且如果dvcl.cpp只編譯一次,那麼這裏沒有什麼可質疑的。

我希望更有經驗的同事可以清除我的疑惑(我很抱歉發了很長的帖子)。

回答

2
  1. 「靜態SampleNamespace :: MyClass myClass;」既是聲明又是定義。所以你的構造函數被調用和創建。用於實例化的Asm代碼是在編譯時生成的,但是這是在可執行文件加載時執行的。

對於referenec,彙編 源代碼級 - >預處理 - >彙編 - >聯 - >加載 - >執行

  • 是,「.c/.cpp」文件只編譯一次。但是每個包含頭文件都會被解析。

  • 是的對象只被執行一次,因爲相應的對象文件被鏈接並加載一次。

  • 1

    第一點:

    dvcl編譯單元是一個靜態庫。如果未使用該代碼,則編譯對象(.o)不包含在生成的可執行文件中。因此,static SampleNamespace::MyClass myClass;永遠不會執行。如果您正在使用動態庫或在鏈接時明確鏈接.o文件,它將不會相同。

    觀點二:

    使用你與否庫,源文件(.C)或(的.cpp)只編譯(並鏈接到可執行文件)一次。這對於具有.H是包括在其他文件,因此每處理包括文件

    一次文件的原因

    第三點:

    的對象實例化有效的一次,因爲.o文件將被鏈接只有一次