在 C++ 中,静态对象可以比其静态成员变量长寿吗?

In C++, can a static object outlive its static member variable?

关于C++中静态变量的析构顺序,是否对静态对象的静态成员变量的生命周期有任何保证?

例如,如果我有这样的东西(仅用于演示目的的疯狂简化示例):

class Object {
    static std::vector< Object * > all_objects;

public
    Object() {
        all_objects.push_back( this );
    }

    ~Object() {
        all_objects.erase(
          std::remove(all_objects.begin(), all_objects.end(), this), 
          all_objects.end());
    }
};

对于不同编译单元中的静态对象,这会是"safe"吗?也就是说,是否可以保证 all_objects 成员变量至少与任何有效对象一样长,或者是否存在 all_objects 在最后一个对象实例之前被销毁的问题?

如果代码用作库(比如在 Python 内)而不是作为具有自己的 main() 的独立程序,答案会改变吗?

Would this be "safe" with respect to static Objects in different compilation units?

不,这不安全。

这是一个不安全的示例,因为无法保证静态数据初始化的相对顺序。不过,有一些特定于平台的方法可以实现这一点。

See the FAQ for techniques to work around this static initialisation fiasco。该技术基本上将静态成员隐藏在函数中,然后在首次使用时对其进行初始化。

只要添加到静态成员的对象得到适当管理,即不是静态的,并且它们不会在某处悬空或处于某种未定义状态并且不会遭受其他未定义行为,它就可以变得安全。

And does the answer change if the code is being used as a library (say within Python) rather than as a standalone program with its own main()?

我认为除了实现定义之外,这不会由标准定义。据我所知,不,鉴于流行的实现、平台及其 ABI 等,答案不会改变。

Would this be "safe" with respect to static Objects in different compilation units?

初始化时不安全。无法保证 all_objects 会在构建编译单元中的 static 对象时被初始化。

我不清楚终止顺序。我的猜测是破坏的发生顺序与建造的顺序相反。如果construction/initialization不安全,那么销毁也可能不安全。

在初始化时使其安全的一种方法是将 all_objects 包装在一个函数中。

class Object {
    static std::vector<Object *>& get_all_objects();

public
    Object() {
        get_all_objects().push_back( this );
    }

    ~Object() {
       std::vector<Object *>& all_objects = get_all_objects();
       all_objects.erase(
          std::remove(all_objects.begin(), all_objects.end(), this), 
          all_objects.end());
    }
};

std::vector<Object *>& Object::get_all_objects()
{
    static std::vector<Object *> all_objects;
    return all_objects;
}

这就是 C++11 标准 (3.6.3/1) 关于销毁具有静态存储持续时间的对象的说法。

If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first.

鉴于上述方法销毁是安全的all_objects只有在最后一个Object被销毁后才会被销毁。

静态变量确实具有全局作用域,因为它们不在函数或方法的堆栈上。所以在最后可能的时间调用析构函数。

所以在单线程环境下我看不出有什么问题。这是一个愚蠢的例子,但确实如此 运行。在 return 语句之后调用两个静态变量的析构函数。

ob.h
class ob
{
  static int a;
  static int b;
public:
  ob()
  {
    a++;
  }
  ~ob()
  {
    b--;
  }
};

main.cpp #include ob.h;

int ob::a = 0;
int ob::b = 0;

void tt()
{
  static ob zz;
}

int main() 
{
  static ob yy;
  tt();

  {
    ob b;
  }
  ob a;

  return 1;
}

关于另一个编译单元中的静态变量,将取决于您如何使用它。例如,如果所有内容都是内联的,并且 header 用于 A.dll 和 B.dll,它们之间将没有引用,您必须在每个单元中初始化静态,为它们提供唯一的地址。但是如果它在导出它的 lib 或 dll 中,您将使用相同的内存地址。 我之前看到过一个问题,我们有两个相同的版本 class。 Object A 为 1.0,object B 为 1.2。它们没有直接导出,而是用于导出的函数。 object 调用了错误的析构函数。那是一个非常糟糕的编码选择并且被改变了。 在 multi-threaded 构建中,它可能非常糟糕,具体取决于您使用 object 的方式。你不知道销毁的顺序,你可以尝试在它被销毁后访问它。

总的来说,我认为这不是一个好的做法。它会起作用,但在更复杂的情况下,未来的变化可能会很快破坏事情。

为了完成@Niall 的回答,虽然初始化顺序未定义,但销毁顺序将与初始化顺序相反。

确定任何事情的唯一方法是创建一个全局函数,将您的对象作为静态局部变量(如其他答案所示)。

在那种情况下,您肯定会知道 static 对象将被删除 "before" 静态 class 成员,因为它是 "after" 创建的(第一次调用全局函数):

class Object {
    static std::vector< Object * > all_objects;

public
    Object() {
        all_objects.push_back( this );
    }

    ~Object() {
        all_objects.erase(
          std::remove(all_objects.begin(), all_objects.end(), this), 
          all_objects.end());
    }
};

Object& static_obj()
{
     static Object obj;
     return obj;
}

std::vector< Object * > Object::all_objects; // It is created first (before main)

int main()
{
    Object& o = static_obj(); // `obj` is initialized here.
} // At the end of the program, `obj` will be destroid first.