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文件将被链接只有一次