Estoy intentando crear un Tutorial sobre una de las variantes del Patrón de diseño Singleton usado en C++ que se conoce como Singleton estático. Te paso la información que aparece en el libro que estoy usando. 
Static singleton
One of the simplest implementations of the singleton pattern is a static singleton. In this implementation, the object has only static data members:

class Singleton {
 public:
 Singleton() {}
 int& get() { return value\_; }
 private:
 static int value\_;
};
int Singleton::value\_ = 0;
For the rest of this section, we will consider a singleton with one integer data member, and the member functions that provide access to it. We do this only for the sake of a concrete example—the real-life implementation can have an arbitrary number of data members of different types, and an arbitrarily complex API that's provided by the member functions.

The member functions may be static or not. If the member functions are not static, the implementation may be classified as a multiple handle objects, one data type, because to access the singleton, the programmer simply constructs a Singleton object as many times as needed:

Singleton S;
int i = S.get();
++S.get();
The object may even be constructed on the fly, as a temporary variable:

int i = Singleton().get();
++Singleton().get();
There is no actual construction, that is, no code is generated or executed at runtime to construct this object—after all, there is nothing to construct, as all data is static. We can expect this singleton to be very fast—an expectation that is confirmed by the benchmark:

#define REPEAT(X) ... repeat X 32 times
void BM\_singleton(benchmark::State& state) {
 Singleton S;
 for (auto \_ : state) {
 REPEAT(benchmark::DoNotOptimize(++S.get());)
 }
 state.SetItemsProcessed(32\*state.iterations());
}
In this benchmark, we use the REPEAT macro to generate 32 copies of the benchmarked code inside the loop; this is done to reduce the overhead of the loop, since each iteration is very short:
The alternative use of the singleton, where a temporary object is created every time, can be benchmarked just as easily:

void BM\_singletons(benchmark::State& state) {
 for (auto \_ : state) {
 REPEAT(benchmark::DoNotOptimize(++Singleton().get());)
 }
 state.SetItemsProcessed(32\*state.iterations());
}
We expect it to be equally fast—an expectation that is confirmed by the following measurements:
As far as the lifetime of the singleton goes (not the handle object, the data itself), it is initialized together with all the static data in the program, some time before main() is called. It is destroyed at some point at the end of the program, after main() exits.

An alternative way is to declare not just data members, as static, but also function members, as shown here:

class Singleton {
 public:
 static int& get() { return value\_; }
 private:
 Singleton() = delete;
 static int value\_;
};
int Singleton::value\_ = 0;
In this approach, we never have to construct the Singleton object at all:

int i = Singleton::get();
++Singleton::get();
This implementation can be classified as a zero handle objects, one data type. However, in every way that matters, it is identical to the previous one—the exact syntax of the function calls is different, but what they do is the same, and so is the performance.

An important consideration is the thread safety of the singleton implementation, as well as the performance in a concurrent program. The static singleton itself is obviously thread-safe—it is initialized and destroyed by the C++ runtime system, together with the rest of the static data, and no user threads can be running at that time. The thread-safe use of the singleton is always the responsibility of the programmer, of course—if the singleton provides access to modify its data, it must be protected by a mutex, or implemented in a thread-safe way. When we discuss thread safety of the singleton implementation, we focus on the initialization, destruction, and provision of access to the singleton object—these operations are part of the implementation of the pattern; the rest is specific to the program.

The thread safety of the static singleton is trivially assured, but what about performance? In this case, to access the singleton the program, all that needs to be read is the static data, so there is no overhead. Of course, there may be overhead if the program needs to synchronize modifications of this data, but that is no different from any other shared data in a concurrent program.

There are two potential downsides to this implementation. First of all, this singleton cannot be extended through inheritance. This is the less important of the two problems—singleton objects are, or should be, rare enough in a good design, so code reuse is not a significant concern.

The more important problem has to do with the object's lifetime—the singleton is initialized as a static object before the program itself is started. The order of initialization of the static objects is, in general, undefined and left to the implementation—the standard guarantees that all objects defined in one file are initialized in the order of their definitions, but for objects initialized in different files, there are no guarantees at all with regard to the order.

This is not a problem if the singleton is going to be used by the program when it executes; that is, used for anything that is called from main()—the singleton will definitely be constructed before the program starts and will not be destroyed until after it ends. It is, however, a problem for any code in any other static object that uses this singleton. Such dependencies between static objects are not uncommon—for example, if our singleton is the memory manager, and there is only one instance of such in the program, then any static object that allocates memory has to get it from the memory manager. However, unless the whole program is contained in one file, there is no way to guarantee that the singleton memory manager is initialized before its first use. The next implementation attempts to address this problem.
Su contenido no me acaba de convencer, especialmente el ejemplo práctico y la necesidad de usar de Google Benchmark que introduce un factor de complicación en el tutorial que en mi opinión nos aleja del objetivo fundamental que es centrarnos en el patrón Singleton Static o Estático. Me gustaría que rehicieras esta unidad didáctica y crearas un contenido claro y directo con ejemplos de código práctico y funcionales (que tengan una salida en pantalla por ejemplo) de que es y para que sirve el patrón de diseño Singleton Static.
정적 싱글 톤으로 알려진 C++에서 사용되는 싱글 톤 디자인 패턴의 변형 중 하나에 대한 자습서를 만들려고합니다. 제가 사용하고 있는 책에 나오는 정보를 살펴보겠습니다. 정적 싱글톤싱글톤 패턴의 가장 간단한 구현 중 하나는 정적 싱글톤입니다. 이 구현에서는 객체에 정적 데이터 멤버만 있습니다:Singleton 클래스 { public: Singleton() {} int& get() { return value\_; } private: static int value\_;};int Singleton::value\_ = 0;이 섹션의 나머지 부분에서는 하나의 정수 데이터 멤버를 가진 싱글톤과 이에 대한 액세스를 제공하는 멤버 함수를 고려하겠습니다. 이는 구체적인 예시를 위한 것으로, 실제 구현에서는 다양한 유형의 데이터 멤버가 임의의 수로 존재할 수 있으며 멤버 함수가 제공하는 API도 임의로 복잡할 수 있습니다.멤버 함수는 정적일 수도 있고 아닐 수도 있습니다. 멤버 함수가 정적이 아닌 경우, 프로그래머는 싱글톤에 액세스하기 위해 필요한 만큼 싱글톤 객체를 구성하기 때문에 구현은 하나의 데이터 유형인 다중 핸들 객체로 분류될 수 있습니다:싱글톤 S;int i = S.get();++S.get();이 객체는 임시 변수로 즉석에서 생성할 수도 있습니다:int i = Singleton().get();++Singleton().get();이 객체를 구성하기 위해 런타임에 생성되거나 실행되는 코드가 없으므로 실제 구성은 없으며, 결국 모든 데이터가 정적이므로 구성할 것이 없습니다. 이 싱글톤은 매우 빠를 것으로 예상할 수 있으며, 이는 벤치마크에서 확인되었습니다:#정의 REPEAT(X) ... X 32회 반복void BM\_singleton(benchmark::State& state) { 싱글톤 S; for (auto \_ : state) { REPEAT(benchmark::DoNotOptimize(++S.get());) } state.SetItemsProcessed(32\*state.iterations());}이 벤치마크에서는 REPEAT 매크로를 사용하여 루프 내부에 벤치마크 코드의 사본을 32개 생성하는데, 이는 각 반복이 매우 짧기 때문에 루프의 오버헤드를 줄이기 위해 수행됩니다:매번 임시 객체를 생성하는 싱글톤의 다른 사용 방법도 마찬가지로 쉽게 벤치마킹할 수 있습니다:void BM\_singletons(benchmark::State& state) { for (auto \_ : state) { REPEAT(benchmark::DoNotOptimize(++Singleton().get());) } state.SetItemsProcessed(32\*state.iterations());}우리는 똑같이 빠를 것으로 예상하며, 이는 다음 측정값을 통해 확인되었습니다:싱글톤의 수명(핸들 객체나 데이터 자체가 아니라)에 관한 한, 싱글톤은 프로그램의 모든 정적 데이터와 함께 메인()이 호출되기 얼마 전에 초기화됩니다. 메인()이 종료된 후 프로그램이 끝날 때 어느 시점에 소멸됩니다.다른 방법은 다음과 같이 데이터 멤버뿐만 아니라 함수 멤버도 정적으로 선언하는 것입니다:싱글톤 클래스 { public: static int& get() { return value\_; } private: Singleton() = delete; static int value\_;};int Singleton::value\_ = 0;이 접근 방식에서는 싱글톤 객체를 전혀 생성할 필요가 없습니다:int i = Singleton::get();++싱글톤::get();이 구현은 하나의 데이터 유형인 제로 핸들 객체로 분류할 수 있습니다. 그러나 중요한 모든 면에서 이전 구현과 동일합니다. 함수 호출의 정확한 구문은 다르지만 수행하는 작업은 동일하며 성능도 마찬가지입니다.중요한 고려 사항은 싱글톤 구현의 스레드 안전성과 동시 프로그램에서의 성능입니다. 정적 싱글톤 자체는 다른 정적 데이터와 함께 C++ 런타임 시스템에 의해 초기화 및 소멸되며, 이때 사용자 스레드는 실행될 수 없습니다. 물론 싱글톤의 스레드 안전 사용은 항상 프로그래머의 책임입니다. 싱글톤이 데이터를 수정할 수 있는 액세스를 제공하는 경우 뮤텍스로 보호하거나 스레드 안전 방식으로 구현해야 합니다. 싱글톤 구현의 스레드 안전성에 대해 논의할 때는 싱글톤 객체에 대한 초기화, 소멸 및 액세스 제공에 초점을 맞추는데, 이러한 작업은 패턴 구현의 일부이며 나머지는 프로그램에 따라 다릅니다.정적 싱글톤의 스레드 안전성은 사소하게 보장되지만 성능은 어떨까요? 이 경우 프로그램에서 싱글톤에 액세스하기 위해 읽어야 하는 것은 정적 데이터뿐이므로 오버헤드가 없습니다. 물론 프로그램이 이 데이터의 수정 사항을 동기화해야 하는 경우 오버헤드가 발생할 수 있지만, 이는 동시 프로그램의 다른 공유 데이터와 다르지 않습니다.이 구현에는 두 가지 잠재적인 단점이 있습니다. 우선, 이 싱글톤은 상속을 통해 확장할 수 없습니다. 이는 두 가지 문제 중 덜 중요한 문제인데, 싱글톤 객체는 좋은 설계에서는 충분히 드물기 때문에 코드 재사용은 큰 문제가 되지 않습니다.더 중요한 문제는 객체의 수명과 관련된 것으로, 싱글톤은 프로그램 자체가 시작되기 전에 정적 객체로 초기화됩니다. 정적 객체의 초기화 순서는 다음과 같습니다.