编译,静态初始化和静态库的麻烦

时间:2014-11-14 13:40:03

标签: c++ compilation static-libraries static-variables static-initialization

我最近在C ++程序中遇到过一些我无法完全理解的行为。让我通过简单的例子来解释这种行为。

1。 第一个静态库

在层次结构的最底层,我有一个静态库 - 让它命名为 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 文件声明用于操作MyClass对象的简单API, dvcl.cpp 实现此API。请注意,dvcl.cpp文件包含此API的方法使用的MyClass对象的定义,这一点很重要。变量定义为static,因此它只在此源文件中可见。

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 个答案:

答案 0 :(得分:2)

  1. “static SampleNamespace :: MyClass myClass;”既是声明又是定义。因此,您的构造函数被调用并创建。用于实例化的Asm代码是在编译时生成的,但这是在可执行加载时执行的。
  2. 对于referenec,编译阶段 源代码 - &gt;预处理 - &gt;编译 - &gt;链接 - &gt; loading - &gt;执行

    1. 是的,“。c / .cpp”文件只编译一次。但是每个包含都会解析标题。

    2. 是对象只执行一次,因为相应的对象文件只链接并加载一次。

答案 1 :(得分:1)

第一点:

dvcl编译单元位于静态库中。如果未使用代码,则编译对象(.o)不包含在生成的可执行文件中。因此,static SampleNamespace::MyClass myClass;永远不会被执行。如果您在链接时使用动态库或明确链接.o文件,则不一样。

第二点:

使用或不使用库,源文件(.c)或(.cpp)仅编译(并链接到可执行文件)一次。这就是在其他文件中包含包含的.h文件的原因,因此每个包含文件一次处理

第三点:

该对象有效地实例化一次,因为.o文件只链接一次