Проблемы с компиляцией, статической инициализацией и статическими библиотеками

0

Недавно я столкнулся с поведением в программе 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 объявляет простой API, используемый для манипулирования объектом MyClass, а dvcl.cpp реализует этот API. Важно заметить, что файл dvcl.cpp содержит определение объекта MyClass, используемого методами этого API. Переменная определяется как статическая, поэтому она будет видна только внутри этого исходного файла.

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++ будет компилировать каждый исходный файл только один раз, независимо от того, сколько из них содержит соответствующий заголовочный файл.
  • Объект myClass, определенный в dvcl.cpp, создается только один раз. Если я правильно понял второй пункт, и если dvcl.cpp скомпилирован только один раз, то здесь нечего беспокоиться.

Я надеюсь, что более опытные коллеги могут убрать мои сомнения (и я прошу прощения за очень длинный пост).

Теги:
static-libraries
compilation
static-variables

2 ответа

2
Лучший ответ
  1. "static SampleNamespace :: MyClass myClass;" - это декларация и определение. Поэтому ваш конструктор вызывается и создается. Асм-код для создания экземпляра создается во время компиляции, но это выполняется во время загрузки исполняемого файла.

Для referenec, этапы компиляции исходный код → предварительная обработка → компиляция → ссылка → погрузка → выполнение

  1. Да, файл ".c/.cpp" скомпилирован только один раз. Но заголовок анализируется за включение.

  2. Да объект выполняется только один раз, потому что соответствующий объектный файл связан и загружается только один раз.

1

Первый пункт:

dvcl компиляции dvcl находится в статической библиотеке. Если код не используется, скомпилированный объект (.o) не включается в результирующий исполняемый файл. Таким образом, static SampleNamespace::MyClass myClass; никогда не выполняется. Это будет не то же самое, если вы используете динамическую библиотеку или явно связываете файл.o во время ссылки.

Второй момент:

Используйте библиотеки или нет, исходный файл (.c) или (.cpp) только скомпилирован (и связан с исполняемым файлом) один раз. Это причина наличия файлов.h, которые включены в другие файлы и как таковые обрабатываются один раз за каждый файл

Третий пункт:

Объект эффективно создается один раз, поскольку файл.o связан только один раз

Ещё вопросы

Сообщество Overcoder
Наверх
Меню