【问题标题】:How a function-scoped static variable may lead to incompatibility with future uses of the function's code located in shared library函数范围的静态变量如何导致与共享库中函数代码的未来使用不兼容
【发布时间】:2020-03-19 08:16:09
【问题描述】:

HIC++ Coding 标准的规则 3.3.1 限制使用具有静态存储持续时间的变量,即使它们是在块范围内声明的:

具有静态存储持续时间的块作用域对象的初始化顺序是明确定义的。但是,此类对象的生命周期在程序终止时结束,这可能与代码的未来使用不兼容,例如作为共享库。

Application const & theApp()  
{  
  static  Application  app; // Non-Compliant  
  return  app;  
}

问题是会发生什么不兼容问题。

UPD。在我从@Employed-Russian 得到合理的评论后,我意识到需要进行一些澄清。我可以想象多进程访问静态变量的一些问题。例如,在某些 Linux 实现中,相同的内存与分叉进程共享,直到第一次内存写入。它调用写时复制。因此,如果我们在这样的系统上执行以下代码

#include <iostream>
#include <unistd.h>

using namespace std;

struct A
{
    A() {cout << __FUNCTION__ << '\n';}
    ~A() {cout << __FUNCTION__ << '\n';}
};

static void f()
{
    static A a;
}

int main()
{
    f();

    fork();    

    return 0;
}    

我们可以得到类似的输出

A
~A
~A

就是这样,析构函数的双重调用和构造函数的单次调用。这可能不应该是因为在其他系统上我们可以得到A ~A A ~A。所以我们可以想象一些静态变量的共同问题,但是共享库和块范围的静态变量的特殊问题是什么?

【问题讨论】:

    标签: c++ shared-libraries static-variables


    【解决方案1】:

    问题是会发生什么不兼容问题。

    例如,在调用exit 之前并非所有线程都终止的多线程程序中,该库可能变得不可用。

    也就是说,如果线程T0 调用了exit,那么Application 析构函数将在某个时候被调用(因为它在第一次调用theApp() 时注册了atexit)。

    如果线程 T1 仍在运行,并且具有对 theApp.app 的引用,那么它现在将具有对被破坏对象的引用,并且可能会崩溃。

    这被称为退出竞争,并且可能是高度不可重现的(如果T0T1 有机会崩溃之前达到sys_exit,您将观察到正常退出)。

    更新:

    析构函数的双重调用。这不应该是

    误会:这完全正确正常工作。这里没有对析构函数的双重调用:你有两个进程,每个进程都会析构自己的对象(应该如此)。

    【讨论】:

    • 好点,但正如我所见,这不仅仅是共享库的问题。如果我们直接在我们的应用程序中定义 theApp,我们会得到相同的结果,不是吗?
    • @chimpp 是的,您可以在应用程序中以同样的方式射击自己。但是(与应用程序开发人员不同),作为库编写者,您(通常)无法控制哪些应用程序将使用它。这适用于所有图书馆,我看不出有什么特别之处/为什么他们特别提到 shared 图书馆。
    • @chimpp 我已经更新了答案以解决您的“双重析构函数”点。
    • You are mistaken: this is working exactly as it should. 感谢您的评论。我也更新了问题)关键是我们在不同的系统上有不同的行为,这可能是不应该的。
    • @chimpp 你在这一点上也错了:每个现存的 UNIX 系统都会有 same(正确的)行为。
    猜你喜欢
    • 2016-06-23
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 2016-11-25
    • 1970-01-01
    • 2011-06-11
    • 2021-02-02
    • 1970-01-01
    相关资源
    最近更新 更多