【问题标题】:On Linux, why does the destructor run twice on shared instance of global variable in C++?在 Linux 上,为什么析构函数在 C++ 中全局变量的共享实例上运行两次?
【发布时间】:2010-08-04 19:19:52
【问题描述】:

在 Linux 上,我有一些从定义全局变量的静态库生成的 C++ 代码。此全局变量的单个实例在引用其符号的两个共享库之间共享。

当进程关闭并运行静态终止阶段时,我看到此共享实例上的析构函数运行了两次!每次卸载时大概每个库一次。

这个问题与我最近在这里看到的另一个问题密切相关:related question。这听起来像是相同的行为,但没有讨论为什么会发生这种情况。

有人知道这种行为背后的理论解释吗?

【问题讨论】:

  • 编译器生成的代码每个变量只能运行一次。尝试在运行析构函数时获取变量的地址,以验证它是否是同一个对象。
  • 也许你在某个智能指针类中存储了一个指向对象的指针,它试图破坏它的目标。
  • 那么全局只定义在一个库中(一个编译单元)?
  • 请准确解释您是如何构建事物的,以便“在引用其符号的两个共享库之间共享此全局变量的单个实例”。
  • 地址肯定是一样的。我可以看到构造函数和析构函数针对相同的内存位置运行了两次。我实际上认为全局是在两个库的编译单元中定义的,尽管我不清楚为什么会这样。该变量在静态库 (.a) 中定义,并且两个共享库都与该存档链接。定义在 .cpp 文件中,对应的 .h 文件只包含一个“extern”声明。有没有很好的资源来详细说明链接器如何确定在构建时从 .a 中提取哪些定义?

标签: c++ linux shared-libraries global-variables destructor


【解决方案1】:

如果你把一个裸指针放在一个智能指针中(两次),它会破坏两次,每个容器引用计数降为零一次。

因此,如果您将裸指针传递给两个库,就可以了。每个都将它放在一个共享指针对象中,并且每个都进行破坏。如果您可以在 dtor 期间看到堆栈回溯,那应该表明它在两个库中都发生了。

【讨论】:

  • 使用任何常规调试器,应该很容易检查调用堆栈回溯并找出谁在调用这个析构函数。
【解决方案2】:

C++ 有一个规则叫做“一个定义规则”:

每个程序都应包含该程序中使用的每个非内联函数或对象的准确定义;无需诊断。定义可以显式出现在程序中,可以在标准或用户定义库中找到,或者(在适当时)隐式定义(参见 12.1、12.4 和 12.8)。

维基百科有一个article 更详细地解释了这一点。

您尚未在问题中发布代码,因此我无法确定您的情况,但在the question you linked to 中,问题中的示例是在两个共享库中定义相同的变量。这违反了“一个定义规则”,显然动态链接器的终结策略依赖于该规则,导致析构函数被调用两次。

【讨论】:

    猜你喜欢
    • 2019-09-03
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    相关资源
    最近更新 更多