Недавно я столкнулся с поведением в программе 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. Мои сомнения и вопросы:
Я надеюсь, что более опытные коллеги могут убрать мои сомнения (и я прошу прощения за очень длинный пост).
Для referenec, этапы компиляции исходный код → предварительная обработка → компиляция → ссылка → погрузка → выполнение
Да, файл ".c/.cpp" скомпилирован только один раз. Но заголовок анализируется за включение.
Да объект выполняется только один раз, потому что соответствующий объектный файл связан и загружается только один раз.
Первый пункт:
dvcl
компиляции dvcl
находится в статической библиотеке. Если код не используется, скомпилированный объект (.o) не включается в результирующий исполняемый файл. Таким образом, static SampleNamespace::MyClass myClass;
никогда не выполняется. Это будет не то же самое, если вы используете динамическую библиотеку или явно связываете файл.o во время ссылки.
Второй момент:
Используйте библиотеки или нет, исходный файл (.c) или (.cpp) только скомпилирован (и связан с исполняемым файлом) один раз. Это причина наличия файлов.h, которые включены в другие файлы и как таковые обрабатываются один раз за каждый файл
Третий пункт:
Объект эффективно создается один раз, поскольку файл.o связан только один раз