15

Używam C++ w Visual Studio 2015 sp3. PrzezCzy mogę zmienić kolejność inicjalizacji zmiennej statycznej w C++?

#pragma init_seg(compiler) 

, zainicjować niektóre zmienne statyczne pierwszy (do zarządzania pamięcią). https://msdn.microsoft.com/en-us/library/7977wcck.aspx

Ale jest

#pragma init_seg(compiler) 

w wcerr.cpp (Microsoft Visual Studio 14.0 \ VC \ crt \ src \ stl \ wcerr.cpp), więc obiekty te są inicjowane przed moje obiekty.

Czy mogę zmusić mój obiekt do zainicjowania najpierw przed obiektami wcerr.cpp przez dowolne opcje kompilacji/linku?

+0

Dlaczego zamówienie ma znaczenie? –

+0

@ Cheersandhth.-Alf Ponieważ mój obiekt inicjuje niestandardowy system pamięci, jeśli niektóre inne obiekty wywołują nowe przed moim obiektem, powoduje to błąd. :( –

+4

Tak więc aktualne pytanie brzmi - [jak zastąpić przydział pamięci w MSVC] (http://stackoverflow.com/questions/12815867/overriding-memory-allocator-in-msvc) – rustyx

Odpowiedz

12

Jednym z rozwiązań jest, aby spróbować owinąć zmiennych statycznych funkcji statycznych:

static type& My_static_obj() { 
    static type my_static_obj_; 
    return my_static_obj_; 
} 

Wygląda na to prosty typ Singleton i wzywa Construct przy pierwszym użyciu Idiom. Ze względu na standard (C++ 11 i wyżej) gwarantowano, że zostanie zainicjowany raz (a nawet atomowo!), A wewnątrz jego obiektu taki obiekt może uzyskać dostęp do innych zmiennych "statycznych", więc jeśli nie ma w nim cyrkulacji zależności między zmiennymi, kolejność inicjalizacji zostanie ściśle określona.

Aby uzyskać dodatkowe informacje, patrz this question i inne opisy tego Idiomu konstrukcyjnego na pierwsze użycie.

+0

"Zbuduj na pierwszym użyciu idiomu" to dobra informacja.Dzięki. Ale muszę skonstruować obiekt przed werr w Microsoft Visual Studio 14.0 \ VC \ crt \ src \ stl \ wcerr.cpp. Czy mogę użyć tego idiomu do wcerr? –

+0

[Standard] (http: // en.cppreference.com/w/cpp/io/cerr) mówi, że (_possible_) może zostać osiągnięty przez włączenie '' _ po tym, jak twój obiekt inicjalizuje (drugi akapit, powoduje 'std :: ios_base :: Init' [inicjowanie] (http://pl.cppreference.com/w/cpp/io/ios_base/Init)), być może będziesz musiał wykonać deklaracje forward dla '' '' '' '' '' '' '' '' '' - do twoich celów, ale to brudny hack. Spróbuj i daj nam znać. Jeszcze raz: ogólnie rzecz biorąc, jak wspomniano, zamówienie nie jest zdefiniowane i wydaje się, że VS a nd Win spróbuje zainicjować swoje narzędzie przed jakimkolwiek kodem użytkownika. – Trollliar

3

Prawdopodobnie nifty counter idiom może pomóc jakoś w tym przypadku:

Zapewnienie non-local obiekt statyczny jest zainicjowana przed jego pierwszym użyciem i zniszczone dopiero po ostatnim użyciu obiektu.

Jej motywacja jest dość oczywista:

Gdy obiekty statyczne korzystać z innych obiektów statycznych, problem inicjalizacji staje się bardziej złożona. Obiekt statyczny musi zostać zainicjowany przed jego użyciem, jeśli ma nietrywialną inicjalizację. Kolejność inicjowania obiektów statycznych w jednostkach kompilacji nie jest dobrze zdefiniowana. Wiele obiektów statycznych, rozmieszczonych w wielu jednostkach kompilacji, może używać pojedynczego obiektu statycznego. Dlatego należy go zainicjować przed użyciem. Jednym z przykładów jest std :: cout, który jest zwykle używany przez wiele innych obiektów statycznych.

Warto go skopiować i wkleić bezpośrednio przykład z powyższej połączonej strony:

Stream.h

#ifndef STREAM_H 
#define STREAM_H 

struct Stream { 
    Stream(); 
    ~Stream(); 
}; 
extern Stream& stream; // global stream object 

static struct StreamInitializer { 
    StreamInitializer(); 
    ~StreamInitializer(); 
} streamInitializer; // static initializer for every translation unit 

#endif // STREAM_H 

Stream.CPP

#include "Stream.h" 

#include <new>   // placement new 
#include <type_traits> // aligned_storage 

static int nifty_counter; // zero initialized at load time 
static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type 
    stream_buf; // memory for the stream object 
Stream& stream = reinterpret_cast<Stream&> (stream_buf); 

Stream::Stream() 
{ 
    // initialize things 
} 
Stream::~Stream() 
{ 
    // clean-up 
} 

StreamInitializer::StreamInitializer() 
{ 
    if (nifty_counter++ == 0) new (&stream) Stream(); // placement new 
} 
StreamInitializer::~StreamInitializer() 
{ 
    if (--nifty_counter == 0) (&stream)->~Stream(); 
} 

Plik nagłówkowy klasy Stream musi być włączone zanim funkcja składowa może być wywołana na obiekcie Stream. Instancja klasy StreamInitializer znajduje się w każdej jednostce kompilacji. Każde użycie obiektu Stream następuje po włączeniu nagłówka, co zapewnia wywołanie konstruktora obiektu inicjalizacyjnego przed użyciem obiektu Stream.

Zobacz link powyżej, aby uzyskać więcej informacji.

1

EDYTOWANIE: Myślę, że rozwiązaniem, którego potrzebujesz, jest użycie innej implementacji STL, jeśli potrzebujesz używać MSVC 2015, a także unikaj ponownego wdrażania obecnego hackowania zarządzania pamięcią (przykro to nazywać to, ale tak to brzmi, ale potem ponownie użyłem kilku własnych hacków do pracy z MSVC).

Nigdy nie używałem implementacji STL, która nie była dołączona do środowiska programistycznego, ale jest to możliwe. Powinna to być prosta kwestia ustawienia ścieżek dołączania i bibliotek do alternatywnej implementacji STL. Możecie zacząć próbując STLPort

W przeciwnym razie, mój oryginalny porady dotyczące pytanie, przed ostatnim EDIT:

Static kolejność inicjalizacji w jednostce kompilacji (plik cpp) jest kolejność, w którym deklaracje są wykonane. Statyczny porządek inicjalizacji między jednostkami kompilacji jest niezdefiniowany. Nie używaj inicjalizacji statycznej do rozwiązania tego problemu.

Wygląda na to, że RustyX wskazuje w dobrym kierunku komentarzem do Overriding memory allocator in MSVC++.

EDYCJA: Wygląda na to, że szukana odpowiedź może być tutaj: How to properly replace global new & delete operators. Zauważ, że obie odpowiedzi są istotne, będziesz chciał zastąpić new, delete, new[] i delete[]. Ponieważ jest to stosowane podczas łączenia, najwyraźniej system Windows nie użyje ich do wczytanych bibliotek DLL (zobacz komentarze do pierwszej odpowiedzi).

+0

Ponieważ kod zarządzania pamięcią nie jest tylko mój, ale w frameworkach. Przed MSVC 2015, zawsze budował przed wcerr. Nawet w MSVC 2015, w kompilacji Debug mój statyczny obiekt jest konstruowany przed wcerr. Tylko w wersji Release build porządek budowy obiektu został zmieniony. –

+0

W takim razie brzmi to jak problem z kompatybilnością. Miałeś szczęście w poprzednich wersjach MSVC, powiedziałbym, że nie używam MSVC 2015. Używałeś specjalnej dyrektywy kompilatora, na której nie powinieneś polegać z powodów, o których mówiłem o statycznej inicjalizacji zamówienia. Zmieniają się kompilatory (i biblioteki). Jesteś jedyną opcją korzystania z MSVC 2015 Myślę, że nie używam STL Microsoftu, ani nie modyfikuję go i nie tworzę własnego (ale nie sądzę, że jest to licencjonowane w tym celu). Boost ma na przykład renomowaną implementację STL. –

0

możesz uzyskać pomysł z projektu Chromium Docs. Zobacz sekcję "Singleton & base :: LazyInstance".

Powiązane problemy