Proto by mohl statický datový člen nedostane inicializovat?

hlasů
5

Snažím se zaregistrovat spoustu tříd s továrnou v době zatížení. Moje strategie je využít statickou inicializaci, aby se ujistil, že před zahájením main (), továrna je připravena jít. Tato strategie se zdá fungovat, když jsem propojit svůj knihovny dynamicky, ale ne když jsem propojit staticky; když jsem propojit staticky, jen někteří z mých statických datových členů inicializaci.

Řekněme, že moje továrna staví auta. Mám CarCreator třídy, které mohou instancí hrstka vozů, ale ne všechny. Chci továrna shromažďovat všechny z těchto tříd CarCreator takže kód hledá nové auto může jít do továrny, aniž by museli vědět, kdo bude dělat skutečné konstrukci.

Tak jsem se dostal

CarTypes.hpp

enum CarTypes
{
   prius = 0,
   miata,
   hooptie,
   n_car_types
};

MyFactory.hpp

class CarCreator
{
public:
   virtual Car * create_a_car( CarType ) = 0;
   virtual std::list< CarTypes > list_cars_I_create() = 0;
};

class MyFactory // makes cars
{
public:
   Car * create_car( CarType type );
   void factory_register( CarCreator * )

   static MyFactory * get_instance(); // singleton
private:
   MyFactory();

   std::vector< CarCreator * > car_creator_map;
};

MyFactory.cpp

MyFactory:: MyFactory() : car_creator_map( n_car_types );

MyFactory * MyFactory::get_instance() {
   static MyFactory * instance( 0 ); /// Safe singleton
   if ( instance == 0 ) {
      instance = new MyFactory;
   }
   return instance;
}

void MyFactory::factory_register( CarCreator * creator )
{
   std::list< CarTypes > types = creator->list_cars_I_create();
   for ( std::list< CarTypes >::const_iteator iter = types.begin();
         iter != types.end(); ++iter ) {
      car_creator_map[ *iter ] = creator;
   }
}

Car * MyFactory::create_car( CarType type ) 
{
   if ( car_creator_map[ type ] == 0 ) { // SERIOUS ERROR!
      exit();
   }
   return car_creator_map[ type ]->create_a_car( type );
}

...

Pak budu mít zvláštní vozy a konkrétní tvůrce automobilů:

Miata.cpp

class Miata : public Car {...};

class MiataCreator : public CarCreator {
public:
   virtual Car * create_a_car( CarType );
   virtual std::list< CarTypes > list_cars_I_create();
private:
   static bool register_with_factory();
   static bool registered;
};

bool MiataCreator::register_with_factory()
{
   MyFactory::get_instance()->factory_register( new MiataCreator );
   return true;
}

bool MiataCreator::registered( MiataCreator::register_with_factory() );

...

Pro zopakování: dynamicky propojení mé knihovny MiataCreator :: registrovaných dostane inicializovat staticky propojení mé knihovny, to nebude mít inicializován.

Se statickou verzi, když někdo jede do továrny požádat o Miata, Miata prvek car_creator_mapbude ukazovat na NULL a program bude ukončen.

Je tu něco zvláštního s private static integrovanými datové členy, aby jejich inicializace bude nějak přeskočí? Jsou statické datové členy inicializovat pouze tehdy, pokud je použit class? Mé CarCreator třídy nejsou deklarovány v libovolném souboru záhlaví; žijí zcela v rámci souboru cpp. Je možné, že kompilátor inlining inicializační funkce a nějak vyhnout volání MyFactory :: factory_register?

Je tam lepší řešení tohoto problému registraci?

Není k dispozici možnost vypsat IVeškeré z CarCreators v jedné funkci, každý z nich zaregistrovat explicitně s továrnou, a pak zajistit, že funkce se nazývá. Především chci propojit několik knihoven dohromady a definují CarCreators v těchto samostatných knihovnách, ale i nadále používat k jedinému továrnu jim postavit.

...

Zde jsou některé odpovědi jsem předvídat, ale které neřeší můj problém:

1) Váš ojedinělým Factory není závit bezpečné. a) V případě, nevadí, já pracuji jen s jedinou nití.

2) Váš ojedinělým Factory může být inicializována, když jsou vaše CarCreators inicializaci (tedy máte statický inicializační fiasko) a) jsem pomocí bezpečného verzi třídě ojedinělým tím singleton instance do funkce. Pokud by se jednalo o problém, měl bych vidět výstup, pokud jsem přidal příkaz print k MiataCreator's::register_with_factorymetodě: Já ne.

Položena 19/08/2009 v 16:42
zdroj uživatelem
V jiných jazycích...                            


6 odpovědí

hlasů
7

Myslím, že máte statickou inicializaci objednávek fiasko, ale ne s Factory.

To neznamená, že je to registrovaná příznak není stále inicializuje, je to prostě nedostanou inicializaci dost brzy.

Nemůžete spoléhat na statickou inicializaci, aby s výjimkou do té míry, že:

  1. Statické proměnné definované v souboru cpp () stejný překlad jednotky se inicializuje v uvedeném pořadí
  2. Statické proměnné definované v jednotce překlad se inicializuje před jakoukoli funkci nebo způsob v této jednotce překlad je vyvolána poprvé.

Co si nemůže spoléhat na je, že statická proměnná se inicializuje před funkce nebo metoda v nějaké jiné jednotce překlad je vyvolána poprvé.

Zejména, nemůžete spoléhat na MiataCreator :: registrováno (definované v Miata.cpp) se inicializuje před MyFactory :: create_car (definované v MyFactory.cpp) je vyvolána poprvé.

Stejně jako všechny nedefinované chování, někdy dostanete, co chcete, a někdy ne, a nejpodivnější nejvíce zdánlivě nesouvisející věci (jako statické a dynamické propojování) může změnit to, zda funguje tak, jak chcete, nebo ne.

To, co potřebujete udělat, je vytvořit statickou metodu přístupový pro registrované příznak, který je definován v Miata.cpp a mít továrna MyFactory získat hodnotu přes tento přístupový objekt. Vzhledem k tomu, přístupový je ve stejné jednotce překlad jako variabilní definice, bude proměnná inicializována v době, kdy přístupový běží. Potom je třeba volat tento přístupový odněkud.

Odpovězeno 19/08/2009 v 17:22
zdroj uživatelem

hlasů
3

Pokud se statická propojení na mysli přidání všech souborů objektů (.o) do binární, která by měla fungovat jako dynamické věci, pokud jste udělali (.A) statickou knihovnu linker nebude spojovat je uvnitř jsou jen použité předměty z vnitřního prostoru statické knihovny jsou spojeny i v tomto případě nikdo se používá explicitně.

Všechny techinques auto-registrace jsou závislé na zatížení časovým kódem a jeho způsobů, jak se vyhnout statické fiasko, jako je funkce, která vytvoří objekt a vrací ji na požádání.

Ale pokud se nepodaří načíst, že někdy to nebude fungovat, spojující objekt soubory dohromady funguje a načítá dynamické knihovny stejně, ale statické knihovny nikdy spojit bez explicitní závislostí.

Odpovězeno 19/08/2009 v 16:53
zdroj uživatelem

hlasů
1

Obecně se statickými libaries linker bude táhnout pouze na .o soubory z této knihovny, jehož hlavní programové odkazy. Vzhledem k tomu, že nejste přihlášení MiataCreator :: registrováno nebo v Miata.cpp opravdu nic, ale spoléhají na statické initiailization spojce ani nebude obsahovat tento kód do vašich exe, pokud je to spojeno ze statického Library-

Zkontrolujte výsledné spustitelný soubor s nm nebo objdump (nebo DUMPBIN pokud jste na windows), zda je kód pro MiataCreator :: registrovaný je vlastně součástí souboru exe při propojení staticky.

Nevím, jak vynutit linker zahrnout každých kousky statické knihovny i když ..

Odpovězeno 19/08/2009 v 17:22
zdroj uživatelem

hlasů
0

Gcc, můžete přidat -Wl,--whole-archive myLib.a --Wl,--no-whole-archive. To přinutí linker zahrnovat objekty, i když ne odkazoval se na. To je však není přenosná.

Odpovězeno 30/01/2010 v 17:46
zdroj uživatelem

hlasů
0

Osobně si myslím, že byste se ponechání ladem konfliktu se spojkou.

Tyto boolean proměnné nejsou používány ‚bool MiataCreator :: registrovaný‘ os spojka je jich táhne od lib do spustitelného souboru (vzpomenout, jestli tam není žádný odkaz na funkci / globální spustitelný linker nebude tahat z nich námitky z lib [vypadá to pouze pro objekty, které jsou v současné době definované ve spustitelný])

Dalo by se přidat nějaké tiskové prohlášení v ‚bool MiataCreator :: register_with_factory ()‘, aby zjistil, jestli to je někdy nazýván. Nebo zkontrolovat symboly ve svém spustitelný ověřit, že je to tam.

Některé věci bych udělal:

// Return the factory by reference rather than pointer.
// If you return by pointer the user has to assume it could be NULL
// Also the way you were creating the factory the destructor was never
// being called (though not probably a big deal here) so there was no
// cleanup, which may be usefull in the future. And its just neater.
MyFactory& MyFactory::get_instance()
{
    static MyFactory   instance; /// Safe singleton 
    return instance;
}

Místo toho, aby v rámci dvoustupňového inicializaci objektu. Které jsem suspec selhává kvůli spojce. Vytvoření instance vaší továrně a získat konstruktor jej zaregistrovat.

bool MiataCreator::register_with_factory()
{
     MyFactory::get_instance()->factory_register( new MiataCreator );
     return true;
}
//
// I would hope that the linker does not optimize this out (but you should check).
// But the linker only pulls from (or searches in) static libraries 
// for references that are explicitly not defined.
bool MiataCreator::registered( MiataCreator::register_with_factory() );

Já bych to následujícím způsobem:

MiataCreator::MiataCreator()
{
    // I would change factory_register to take a reference.
    // Though I would store it internall as a pointer in a vector.
    MyFactory::getInstance().factory_register(*this);
}

// In Cpp file.
static MiataCreator   factory;

Linker ví o C ++ objekty a konstruktérů a měla by vytáhnout všechny globální proměnné jako konstruktérů může mít potenciálně nežádoucí vliv (Vím, že vaše bool dělá stejně, ale vidím některé linkerů optimalizaci, která se).

Každopádně to je moje 2c hodnotu.

Odpovězeno 19/08/2009 v 22:48
zdroj uživatelem

hlasů
0

Když si zkontrolovat, zda je Miata element je uvnitř mapy? Je to před nebo po hlavní?
Jediný důvod, proč jsem mohl myslet přistupuje mapových prvků před main () (jako je globální inicializace), která se může uskutečnit před vznikem MiataCreator :: registrováno (pokud je to v jiné jednotce překlad)

Odpovězeno 19/08/2009 v 17:11
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more