2016-09-29 2 views
0

Ich habe einige Code, wo eine statische Methode aufgerufen wird, und die statische std::unordered_map innerhalb der gleichen Datei ist nicht initialisiert. Ich verstehe die statische Initialisierung zwischen zwei Kompiliereinheiten ist "undefined" und es gibt viele SO Fragen zum Thema; Wenn ich jedoch std::vector verwende, tritt das Problem nicht auf. Auch der Code kann ausführen, aber ich bin verwirrt, warum diese spezifischen kompilieren Bestellungen nicht funktionieren. SO, meine Fragen sind:C++ statische unorderd_map in statischen Methode verwendet wird nicht initialisiert

  1. Es gibt eine andere ist SO Frage (die ich nicht in der Lage gewesen bin zu finden!) Über statische Initialisierung und dynamische Initialisierung von statischen Variablen. Ist dieser Fehler aufgrund std::undored_map tatsächlich eine dynamische Initialisierung?
  2. gibt es eine Möglichkeit, diesen Code zu erhalten, um die std::unordered_map wie ich erwartet zu initialisieren? Ich versuche tatsächlich, eine statische Bibliothek .lib oder .a zu erstellen. Wenn ich die statische Bibliothek verknüpfe, muss sie in der Regel zuletzt ausgeführt werden, und der Fehler tritt auf.
  3. gibt es irgendwelche Problemumgehungen dafür? Eine Option, an die ich gedacht habe, ist die Erstellung eines std::vector und eines std::unordered_map. Verwenden Sie die std::vector, während die std::unordered_map nicht initialisiert ist (über bool _map_is_initialized). Ändern Sie die Initialisierung des std::unordered_map, um explizit dynamisch zu sein, indem Sie eine Funktion aufrufen, die über die Werte in std::vector iteriert, um std::unordered_map zu erzeugen.

Linux

g++ -std=c++1y -g -c thing.cpp 
g++ -std=c++1y -g -c main.cpp 
g++ -g main.o thing.o -o main 
./main 

Dies führt zu einem Floating point exception (core dumped) Fehler. Durch gdb konnte ich herausfinden, dass hashtable_policy.h trys __num % __den; wo __den==0 ist. Unter Verwendung von gdb scheint es, als ob Thing::Things nicht initialisiert ist.

(gdb) break thing.cpp:12 
(gdb) run 
(gdb) print Thing::Things 
No symbol "Things" in specified context. 
(gdb) print thing 
$1 = (Thing *) 0x618c20 

Windows-

cl /EHsc /Zi /c main.cpp 
cl /EHsc /Zi /c thing.cpp 
link /debug main.obj thing.obj 
main 

In meinem eigentlichen Code, führte dies zu einer sehr klaren Segmentierung Fehler; In diesem Beispiel wird jedoch nur ein Popup geöffnet, das besagt, dass die Anwendung fehlgeschlagen ist. ... Ich habe keine bessere Diagnostik gemacht.

-Code

thing.cpp

#include<iostream> 

#include "thing.hpp" 

std::vector<Thing*> Before; // EDIT: added 

std::unordered_map<std::string, Thing*> Thing::Things; 

std::vector<Thing*> After; // EDIT: added 

Thing::Thing(std::string name) : name(name) { 

} 

bool Thing::Register(Thing *thing) { 
    std::cout << "no worries, vectors initialized..." << std::endl; 
    Thing::Before.push_back(thing); // EDIT: added 
    Thing::After.push_back(thing); // EDIT: added 
    std::cout << "added to vectors, about to fail..." << std::endl; 
    Thing::Things[thing->name] = thing; 
    return true; 
} 

thing.hpp

#pragma once 
#include <string> 
#include <unordered_map> 

class Thing { 
public: 
    static std::vector<Thing*> Before; // EDIT: added 

    static std::unordered_map<std::string, Thing*> Things; 

    static std::vector<Thing*> After; // EDIT: added 

    static bool Register(Thing* thing); 

    std::string name; 

    Thing(std::string name); 
}; 

#define ADD_THING(thing_name) \ 
    static bool thing_name## _is_defined = Thing::Register(new Thing(#thing_name)); 

main.cpp

EDIT

Wenn der Auftrag innerhalb einer bestimmten Kompilierung Einheit gewährleistet ist, zu tun, warum static std::vector<Thing*> Thing::Before und static std::vector<Thing*> Thing::After erhält initialisiert, aber static std::unordered_map<std::string, Thing*> Thing::Things nicht?

+0

siehe hier: https://isocpp.org/wiki/faq/ctors#static-init-order – Nim

+0

Related: http://stackoverflow.com/q/211237/335858 – dasblinkenlight

+0

Können Sie die ADD_THING-Makros nach innen verschieben Main()? Oder eine andere Funktion, die von main() aufgerufen wird? – nephtes

Antwort

1

Statische Initialisierung ist schwierig. Wie this answer besagt, bietet der Standard einige Garantien hinsichtlich der Reihenfolge der Initialisierung innerhalb einer einzelnen "Übersetzungseinheit" (normalerweise eine CPP-Quelldatei), aber keinerlei, was die Reihenfolge der Initialisierungen in verschiedenen Übersetzungseinheiten betrifft.

Wenn Sie die Before und After Vektoren zum Code hinzugefügt, beobachtet man, dass im Gegensatz zu den Anrufen zu ordered_map::operator[], die Anrufe zu vector::push_back() nicht den Prozess hat abstürzen und folgerte, dass die Objekte innerhalb einer einzigen Übersetzungseinheit aus, um initialisiert wurden entgegen den Garantien des Standards. Dort ist eine versteckte Annahme, nämlich dass push_back() keinen Absturz verursacht hat, deshalb muss der Vektor initialisiert worden sein. Dies stellt sich als nicht zutreffend heraus: Der Methodenaufruf auf ein nicht initialisiertes Objekt beschädigt mit hoher Wahrscheinlichkeit den Speicher irgendwo, führt jedoch nicht unbedingt zum Absturz. Eine bessere Möglichkeit, zu überprüfen, ob der Konstruktor aufgerufen wird, besteht darin, den Code in einem Debugger auszuführen und Haltepunkte für die Zeilen festzulegen, die die Definitionen der Objekte enthalten, z. B. std::vector<Thing*> Before in thing.cpp. Dies zeigt, dass die Initialisierung wie im Standard vorausgesagt erfolgt.

Die beste Option zur Vermeidung des "Fiaskos", wie beschrieben here, ist "construct on first use". Im Fall des Beispiel-Code, dies eine direkte Nutzung von Thing::Things beinhalten würde ändern, wie diese Zeile:

Thing::Things[thing->name] = thing; 

ein Verfahren, sagt Thing::GetThings(), die das Objekt initialisiert und gibt einen Verweis auf mich.lcs' answer bietet ein Beispiel dafür, aber Vorsicht: Obwohl es das statische Initialisierungsproblem löst, kann die Verwendung eines statischen Bereichs-Objekts ein noch schädlicheres Problem verursachen: Abstürze beim Programm-Exit aufgrund von static deinitialization order. Aus diesem Grund wird das Objekt mit dem new Schlüsselwort Zuteilung bevorzugt:

std::unordered_map<std::string, Thing*>& Thing::GetThings() 
{ 
    static std::unordered_map<std::string, Thing*>* pThings = 
     new std::unordered_map<std::string, Thing*>(); 
    return *pThings; 
} 

Das Beispiel natürlich nie delete ‚d sein, das wie ein Speicherleck sehr viel anfühlt. Aber selbst wenn es kein Zeiger wäre, würde die Deinitialisierung nur beim Herunterfahren des Programms stattfinden. Wenn der Destruktor des Objekts keine wichtigen Funktionen wie das Löschen des Inhalts einer Datei auf dem Datenträger ausführt, ist der einzige Unterschied, der von Bedeutung ist, die Tatsache, dass die Verwendung eines Zeigers die Möglichkeit eines Absturzes beim Beenden verhindert.

3

Wie in den Kommentaren erwähnt, ist die statische Initialisierungsreihenfolge nicht definiert. Wer kennt den Unterschied zwischen Vektor und Karte? Vielleicht initialisiert Ihr Compiler zuerst Klassen mit einer geraden Anzahl von Zeichen in ihrem Namen.

Wenn Sie c++11 oder höher ausführen, ist die statische Initialisierung von lokalen Funktionen garantiert threadsicher. Sie werden initialisiert, wenn die Steuerung zum ersten Mal die Deklarationsanweisung durchläuft.

// Header 
class Thing { 
public: 
    static std::unordered_map<std::string, Thing*>& Things(); 
    static bool Register(Thing* thing); 


// CPP 
std::unordered_map<std::string, Thing*>& Thing::Things() 
{ 
    static std::unordered_map<std::string, Thing*> things; 
    return things; 
} 

Dies wird das erste Mal, wenn Sie für die Things fragen initialisieren und vermeidet alle mögliche Zufälligkeit der statischen Initialisierung.

Verwandte Themen