2015-06-15 16 views
5

Ich versuche eine globale Instanz einer Klasse zu erstellen, deren Konstruktor auf eine globale Variable verweist.C++ - Programmabsturz beim Erstellen einer globalen Instanz der Klasse, deren Konstruktor auf eine globale Variable verweist

Das Programm kompiliert ohne Fehler. Aber wenn es ausgeführt wird, stürzt es auf der Referenz der globalen Variablen ab.

Wie erstelle ich eine globale Instanz dieser Klasse, ohne dass der Konstruktor abstürzt? Hier

ist die SSCCE, die ich gemacht:

/* main.cpp */ 
#include "TestClass.h" 

// I need a global instance of TestClass 
TestClass j; 

int main() 
{ 
    return 0; 
} 

-

/* C.h */ 
#ifndef C_H_INCLUDED 
#define C_H_INCLUDED 

#include <string> 

// global 
extern const std::string S; 

#endif // C_H_INCLUDED 

-

/* C.cpp */ 
#include "C.h" 

#include <string> 

// extern definition of global 
const std::string S = "global string data"; 

-

/* TestClass.h */ 
#ifndef TESTCLASS_H_INCLUDED 
#define TESTCLASS_H_INCLUDED 

class TestClass 
{ 
public: 
    TestClass(); 
}; 

#endif // TESTCLASS_H_INCLUDED 

-

/* TestClass.cpp */ 
#include "TestClass.h" 

#include <iostream> 

#include "C.h" // for S global 

TestClass::TestClass() 
{ 
    std::cout << S << std::endl; // this line crashes the program 
} 

Debugger Meldungen über Absturz:

Program received signal SIGSEGV, Segmentation fault. 
In std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)()() 
#1 0x004014f9 in TestClass::TestClass (this=0x4a0024 <j>) at E:\cpp\externconsttest\TestClass.cpp:9 
E:\cpp\externconsttest\TestClass.cpp:9:117:beg:0x4014f9 
At E:\cpp\externconsttest\TestClass.cpp:9 
#1 0x004014f9 in TestClass::TestClass (this=0x4a0024 <j>) at E:\cpp\externconsttest\TestClass.cpp:9 
E:\cpp\externconsttest\TestClass.cpp:9:117:beg:0x4014f9 

Dieses Beispiel in Operator stürzt < <, aber es stürzt auf jeden Bezug auf S egal wie es referenziert wird.

+0

Es würde viel helfen, wenn Sie die tatsächliche Ausnahme enthalten würden. –

+4

[Statische Initialisierung Reihenfolge Fiasko?] (Http://isocpp.org/wiki/faq/ctors#static-init-order) –

Antwort

4

Ich denke, dass es abstürzt, weil die globalen const std::string S ist noch nicht zu dem Zeitpunkt initialisiert, wenn contructor Ihre TestClass genannt wird.Dies ist ein generelles Problem mit globalen und statischen Variablen in C++: Im Allgemeinen wissen Sie nicht, in welcher Reihenfolge globale und statische Variablen initialisiert werden (tatsächlich werden sie in der Reihenfolge initialisiert, in der Sie Objektdateien an Linker bei der Verknüpfungsstufe übergeben) nicht sehr hilfreich). Es gibt ein paar verschiedene Lösungen für dieses Problem. Einer von ihnen ist:

  1. Erstellen Sie eine Funktion mit einer statischen Variablen in seinem Körper, die einen Verweis auf die Variable zurückgibt (statt nur globale Variable verwenden Sie würde diese Funktion aufrufen). Dies ist simmilar zum Singletons Entwurfsmuster:

    const std::string& get_my_string() { static const std::string S; return S; }

Dann in Ihrem Konstruktor:

TestClass::TestClass() 
{ 
    std::cout << get_my_string() << std::endl; 
} 

get_my_string Aufruf wird die Initialisierung des statischen String zwingen nur einmal (das erste mal die Funktion aufgerufen wird) genau zu der Zeit, wenn Sie es brauchen. Beachten Sie, dass in diesem Beispiel keine Threads berücksichtigt werden (in einer Multithread-Anwendung sollten Sie die Funktion get_my_string() synchronisieren, um die Initialisierung der statischen Zeichenfolge zu schützen).

Ich hoffe, dass hilft.

Übrigens: Sie können ähnliche Probleme mit Ihrem globalen TestClass j bekommen.

So lösen Sie nur die Hälfte des Problems - Initialisierung (Sie wissen immer noch nicht die Reihenfolge der Zerstörung) - in den meisten Fällen ist es aber ausreichend.

Eine andere Option wäre, die Zeichenfolge auf dem Heap (wahrscheinlich mit ähnlichen Ansatz wie oben beschrieben) zu erstellen - Sie müssen nur delete es zu der Zeit Sie wissen, dass es sicher ist, dies zu tun.

1

C++ bietet keine Syntax zur Steuerung der Ctor-Sequenz für globale Scope-Objekte in verschiedenen Kompilierungseinheiten. Außerdem könnte der nächste Build die Sequenz ändern.

Der Mechanismus, den wir verwendet:

  • auf null ptr initialisiert globalen Bereich Zeiger erstellen.

Und

  • Nach Haupt gestartet, aber bevor irgendwelche Themen werden, werden neue Objekte in einer rationalen Ordnung.

Also, in diesem Fall ...

TestClass* j = nullptr; 

int main(...) 
{ 
    // .. other init 
    /* const std::string* */ 
    S = new std::string ("global string data"); 

    // now that S exists, it is ok to intantiate: 
    /* TestClass() */ 
    j = new TestClass; 

    // much more stuff 

    return(0); 
} 

Explizite Steuerung kann immer arbeiten gemacht werden.

Ein weiterer Vorteil - konstante Startzeit.

Eine verwandte Frage - die meisten Musterbuchmuster sind nicht threadsicher. Kombinieren Sie mehrere Threads ohne Kontrolle des globalen Umfangs ctors kann sehr schwierig zu debuggen sein.

Verwandte Themen