C++ static instance of a user-defined class results a double-call to constructor when compiled and linked in separate steps

心已入冬 提交于 2021-01-29 13:09:37

问题


So I have reduced the problem to a very simple program of an empty main() function and a very simple class as follows.

A.cpp

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

test.cpp

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

Now consider 2 options for building this simple program into 2 different executables:

Generating program #1:

Compile with the following command (generate .o files from the .cpp files): g++ -c test.cpp A.cpp

And then link with the following command: g++ test.o A.o -o linkedTest

Generating program #2:

Compile and link at once with the following command: g++ test.cpp -o test

So at this point we have 2 programs next to the source files (alongside the intermediate .o files): linkedTest and test.

Now, running the program test (command ./test) it will execute the constructor of the class A only once and print the text "Inside A()". In contrast, running the program linkedTest (command ./linkedTest) it will execute the constructor of the class A twice!

So my questions are : Why is this happening? Shouldn't the same compiler (at least) generate the same program out of the same source-code? What is exactly happening behind the stage and how to take control over it? Is this an anticipated compiler/linker behavior or it's a (un)known bug?

Any C++ gurus out there who could shed some light on this...?

For your reference, my GCC version is : gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0


回答1:


Remember that static var are defined per-compile unit

In case of :

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

the compiler create 2 obj each of one have his own static var A.

while by building only one obj :

g++ test.cpp -o test

you get one compilation unit and so one definition of A.




回答2:


When you compile both test.cpp and A.cpp, you have two compilation units that both define a variable named a. Since that variable is declared as static, that is legal (otherwise you'd get an error about a being defined twice) and causes two independent variables to be defined with the same name. And since you get two variables, you also get two constructor calls.

When you only define test.cpp, there's only one compilation unit, only one a and thus only one constructor call.

PS: It's generally a bad idea to include source files into each other because it leads to issues like this.




回答3:


It's unusual, and normally a bad idea, to #include a *.cpp file.

But you would get the same behavior if you used a header file like normal, and a second *.cpp file that includes it:

// 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() {}

When compiling the program above in the normal way, there are two "translation units": one for A.cpp, which includes everything in A.hpp, and one in test.cpp, which also includes everything in A.hpp. Outside of any class or function, the keyword static means a definition has "internal linkage", so that it cannot be used from another translation unit, and if another translation unit defines something similar, it's defining a different object or function with the same name. So yes, the program has and automatically creates two objects both named a of type A.

Your original program which was made from both A.cpp and test.cpp which included A.cpp similarly had two translation units, each with its own object named a of type A. The version just compiling test.cpp had just the one translation unit, and one a object.



来源:https://stackoverflow.com/questions/61305770/c-static-instance-of-a-user-defined-class-results-a-double-call-to-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!