C ++ stati c экземпляр пользовательского класса приводит к двойному вызову конструктора при компиляции и связывании в отдельных шагах - PullRequest
0 голосов
/ 19 апреля 2020

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

A. cpp

#include <iostream>
class A {
public:
    A() {std::cout<<"Inside A()"<<std::endl;}
};
static A a;

test. cpp

#include "A.cpp"
int main() {}

Теперь рассмотрим 2 варианта построения этой простой программы в 2 разных исполняемых файла:

Генерация программы # 1:

Скомпилируйте с помощью следующей команды (сгенерируйте файлы .o из файлов. cpp): g++ -c test.cpp A.cpp

И затем свяжитесь с помощью следующей команды: g++ test.o A.o -o linkedTest

Генерация программы # 2:

Скомпилируйте и скомпонуйте сразу с помощью следующей команды: g++ test.cpp -o test

Итак, на данный момент у нас есть 2 программы рядом с исходными файлами (вместе с промежуточными файлами .o): connectedTest и test .

Теперь выполняется программа test ( команда ./test) только один раз выполнит конструктор класса A и напечатает текст "Inside A ()" . Напротив, при запуске программы connectedTest (команда ./linkedTest) она дважды выполнит конструктор класса A !

Итак, мои вопросы: Почему это так? происходит? Разве один и тот же компилятор (по крайней мере) не должен генерировать одну и ту же программу из одного и того же исходного кода? Что именно происходит за сценой и как взять это под контроль? Это ожидаемое поведение компилятора / компоновщика или (неизвестная) ошибка?

Любые гуру C ++, которые могли бы пролить свет на это ...?

Для справки, мой G CC версия: g cc (Ubuntu 7.5.0-3ubuntu1 ~ 18.04) 7.5.0

Ответы [ 3 ]

1 голос
/ 19 апреля 2020

Помните, что stati c var определены для единицы компиляции

В случае:

g++ -c test.cpp A.cpp
g++ test.o A.o -o linkedTest

компилятор создает 2 объекта, каждый из которых имеет свои собственные данные c var A.

, а при создании только одного объекта:

g++ test.cpp -o test

вы получаете одну единицу компиляции и, таким образом, одно определение A.

1 голос
/ 19 апреля 2020

Когда вы компилируете test.cpp и A.cpp, у вас есть две единицы компиляции, которые обе определяют переменную с именем a. Поскольку эта переменная объявлена ​​как stati c, это допустимо (в противном случае вы получите ошибку о том, что a определяется дважды), и две две независимые переменные будут определены с одинаковым именем. И так как вы получаете две переменные, вы также получаете два вызова конструктора.

Когда вы определяете только test.cpp, есть только один модуль компиляции, только один a и, следовательно, только один вызов конструктора.

PS: Обычно плохая идея включать исходные файлы друг в друга, потому что это приводит к таким проблемам.

0 голосов
/ 19 апреля 2020

Это необычно и, как правило, плохая идея для #include *. cpp файла.

Но вы получите то же поведение, если будете использовать заголовочный файл, как обычный, и второй * . cpp файл, который включает его:

// A.hpp:
#ifndef TEST_CLASS_A_HPP
#define TEST_CLASS_A_HPP

#include <iostream>
class A {
public:
    A() {std::cout<<"Inside A()"<<std::endl;}
};
static A a;

#endif

// A.cpp:
#include "A.hpp"
// and nothing else!

// test.cpp:
#include "A.cpp"
int main() {}

При обычной компиляции программы, приведенной выше, есть две «единицы перевода»: одна для A. cpp, которая включает все в A. hpp и один в тесте. cpp, который также включает все в A.hpp. Вне любого класса или функции ключевое слово static означает, что определение имеет «внутреннюю связь», поэтому его нельзя использовать из другой единицы перевода, а если другая единица перевода определяет нечто подобное, оно определяет другой объект или функцию с помощью то же имя. Так что да, программа имеет и автоматически создает два объекта с именем a типа A.

Ваша исходная программа, которая была создана как из A. cpp, так и из test. cpp, которая включала A . cpp аналогично имел две единицы перевода, каждая со своим собственным объектом с именем a типа A. Версия, только компилирующая тест. cpp имела только одну единицу перевода и один a объект.

...