Eu tenho algum código legado com algumas classes singleton que se registram usando construtores de variáveis globais. É uma grande base de código, compilada em um executável. Tentei organizar a base de código e reagrupar o código em bibliotecas. Um exemplo mínimo do código atual é
main.cpp
int main(int argc, char *argv[])
{
return 0;
}
Hash.cpp
#include <iostream>
class Hash
{
public:
Hash()
{
std::cout << "Hash\n";
}
};
Hash a;
e a configuração de compilação atual é
CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(mcve)
add_executable(mcve main.cpp Hash.cpp)
Construindo o código e executando as impressões executáveis
Hash
Eu modifiquei a configuração de compilação para
cmake_minimum_required(VERSION 3.26)
project(mcve)
add_library(Hash Hash.cpp)
add_executable(mcve main.cpp)
target_link_libraries(mcve Hash)
Isso cria uma biblioteca estática libHash.a
e a vincula ao executável. Compilar o mesmo código e executar o executável não imprime nada. Por que a diferença e onde é descrita? Faz parte do padrão C++ ou do compilador? É específico do sistema operacional (bibliotecas estáticas do Linux)? É um comportamento indefinido?
A diferença deve ser descrita na documentação do seu vinculador, bem como em livros introdutórios, suponho, que explicam o que são bibliotecas estáticas, como funcionam e como usá-las.
Sem bibliotecas estáticas na imagem: quando as unidades de tradução são explicitamente vinculadas, tudo nelas se torna parte do executável.
Em seu primeiro exemplo, ambos
main.cpp
ehash.cpp
estão vinculados aomcve
executável e, na inicialização, o único objeto global dehash.cpp
é construído. O fim.A vinculação com uma biblioteca estática não inclui, repito, não inclui tudo, desde a biblioteca estática até o executável. Não é assim que as bibliotecas estáticas funcionam. Somente unidades de tradução individuais na biblioteca estática que exportam símbolos indefinidos nas unidades de tradução vinculadas à biblioteca estática -- apenas essas unidades de tradução são vinculadas ao executável (é um pouco mais complicado, na verdade, mas toda a complexidade é imaterial para os propósitos desta questão, apenas confundiria as coisas, então trabalharemos apenas com esta descrição simplificada). Essa é uma característica definidora do que são as bibliotecas estáticas.
Um exame muito, muito próximo do código mostrado resulta em uma profunda descoberta de que não há símbolos indefinidos ou não resolvidos
main.cpp
exportados porhash.cpp
. Portanto,hash.cpp
não é vinculado ao executável . O objeto global é definido parahash.cpp
que o executável final não termine com nenhum objeto global que precise ser construído quando o programa for executado. O fim.Não é que as regras de inicialização ou construção mudem para bibliotecas estáticas. É porque não há nada para inicializar ou construir.