C++ 为什么链接器在针对共享和静态链接时不抱怨多个定义

C++ 为什么链接器在针对共享和静态链接时不抱怨多个定义,c++,compilation,linker,C++,Compilation,Linker,我最近在开发的代码中遇到了一个静态初始化失败的问题。这让我意识到我缺乏关于链接过程和全局变量初始化的知识,因此我在cppcon上发现了一个非常有趣的话题,关于全局变量、链接以及将静态库嵌入共享和同时链接两者(定义全局变量)的可能问题。基本上说,当有以下结构时: //static.hpp #pragma once #include <string> extern std::string globalString; //static.cpp #include "stati

我最近在开发的代码中遇到了一个静态初始化失败的问题。这让我意识到我缺乏关于链接过程和全局变量初始化的知识,因此我在cppcon上发现了一个非常有趣的话题,关于全局变量、链接以及将静态库嵌入共享和同时链接两者(定义全局变量)的可能问题。基本上说,当有以下结构时:

//static.hpp
#pragma once

#include <string>

extern std::string globalString;

//static.cpp
#include "static.hpp"

std::string globalString;

//shared.hpp
#pragma once

#include "static.hpp"
#include <string>

std::string& getGlobalString();

//shared.cpp
#include "shared.hpp"
#include "static.hpp"

std::string& getGlobalString(){
  return globalString;
}

//main.cpp
#include "static.hpp"
#include "shared.hpp"
#include <iostream>

int main(){
    std::cout << "Global variable: " << globalString <<  std::endl;
    std::cout << "Global var acccesed through shared lib: " << getGlobalString() << std::endl;
    return 0;
}
由于多次调用同一对象的构造函数和析构函数,导致分段错误。这是令人惊讶的,因为我把它作为不变量,每个对象构造函数将被精确调用一次

  • 关于多次调用构造函数/析构函数的标准是什么
  • 如果在这种情况下,静态i的全局值同时嵌入main.o和libshared.o,那么这是否意味着只有一个定义的规则被打破了?为什么链接器在链接main时不抱怨第二个定义可用
  • 当在单独的翻译单元中至少有两个全局文件时,建立构造和销毁顺序的建议方法之一是在首次使用时初始化。然而,据我所知,这只是确保施工顺序。销毁令呢?如何控制
这是令人惊讶的,因为我把它作为不变量,每个对象构造函数将被精确调用一次

在正确构造的二进制文件中,这将是真的。在具有ODR冲突的二进制文件中不一定如此

关于多次调用构造函数/析构函数的标准是什么

该标准规定,在正确构造的程序中,构造函数/析构函数只调用一次。在ODR冲突的程序中,任何事情都可能发生(未定义的行为)

如果在这种情况下,静态i的全局值同时嵌入main.o和libshared.o,那么这是否意味着只有一个定义的规则被打破了

为什么链接器在链接main时不抱怨第二个定义可用

就链接器而言,您的程序没有问题。从链接器的角度来看,在静态库和共享库中定义一个全局库是完全合乎礼仪的。另见

当在单独的翻译单元中至少有两个全局文件时,建立构造和销毁顺序的建议方法之一是在首次使用时初始化。然而,据我所知,这只是确保施工顺序

对。这是一个不同问题的解决方案:两个全局a和B定义在不同的翻译单元中

你没有这个问题,你有一个完全不同的问题(ODR违规)

销毁令呢?如何控制


在正确构造的程序中,销毁顺序保证与构造顺序相反。

ODR适用于正在编译的程序。可以在程序和链接的共享库中定义相同的符号,而不会出现任何错误。
clang++ -c -std=c++14 -fpic static.cpp
clang++ -c -std=c++14 -fpic shared.cpp
clang++ -c -std=c++14 main.cpp
ar rsc libstatic.a static.o
clang++ -shared -o libshared.so shared.o -L./ -lstatic
clang++ -L./ -Wl,-rpath=./ main.o -lstatic -lshared