问题
Assuming I have the following code
Something.hpp
#pragma once
class Something {
public:
static Something& get();
private:
Something();
};
Something.cpp
#include "Something.hpp"
#include <iostream>
using namespace std;
Something& Something::get() {
static Something something;
return something;
}
Something::Something() {
cout << "Something()" << endl;
}
main.cpp
#include <iostream>
using namespace std;
struct SomethingElse {
~SomethingElse() {
Something::get();
cout << "~SomethingElse" << endl;
}
};
void func() {
static SomethingElse something_else;
// do something with something_else
}
int main() {
func();
return 0;
}
Can more than one instance of the Something
object ever be created? Does the standard say anything about serializing the destruction of static objects?
Note I am aware the the destruction of file level static variables is undefined when across different translation units, I wanted to know what happens in the case of function scoped static variables (which have the double-checked locking pattern built into the C++ runtime) For the same translation unit case with file level static variables, its easy for the compiler to ensure serialization with construction and destruction based on how the variables are laid out in the code (static), but what happens when the variables are dynamically lazily created when the functions are called?
Note What about for primitive variables? Can we expect them to contain their values till program end? Since they don't need to be destroyed.
Edit
Found this on cppreference.com (http://en.cppreference.com/w/cpp/utility/program/exit)
If the completion of the constructor or dynamic initialization for thread-local or static object A was sequenced-before thread-local or static object B, the completion of the destruction of B is sequenced-before the start of the destruction of A
If this is true then destruction for every static object is serialized? But I also found this
https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2 which contradicts the standard
回答1:
The order of construction and destruction of static (and global non-static) objects is well-defined in the C++ specification, for a single translation unit!
If you have multiple translation unit (multiple source files) then the order of construction/destruction between the TUs is not defined.
So the code you show can have undefined behavior.
回答2:
[stmt.dcl] ¶4
Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration.
[basic.start.term] ¶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.
¶2
If a function contains a block-scope object of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behaviour if the flow of control passes through the definition of the previously destroyed block-scope object.
It is in the destructor of SomethingElse
that we risk invoking this undefined behaviour:
SomethingElse::~SomethingElse() {
Something::get();
}
If there is an instance of SomethingElse
with static storage duration, then there are four possibilities:
The single instance of
Something
was constructed before theSomethingElse
. Its destruction will happen after theSomethingElse
, so the behaviour is well defined.The single instance of
Something
was constructed after theSomethingElse
. Its destruction will have happened before theSomethingElse
, so the behaviour is undefined as described above.The single instance of
Something
was constructed in a different thread without being synchronized with respect to the construction of theSomethingElse
. The destructions may happen concurrently, so the behaviour is undefined.No instance of
Something
was yet constructed (i.e. this is the first call toSomething::get
). In this case, the program calls for the construction of aSomething
after theSomethingElse
, which means the destruction of theSomething
must happen before theSomethingElse
, but since the destruction of theSomethingElse
has already commenced, this is a contradiction, and the behaviour is undefined. (Technically, there is a cycle in the "sequenced before" relation.)
来源:https://stackoverflow.com/questions/41486703/referencing-a-possibly-destroyed-static-object