为什么静态数据成员不能被初始化? [英] Why might a static data member not get initialized?

查看:176
本文介绍了为什么静态数据成员不能被初始化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在加载时注册一组工厂的类。我的策略是利用静态初始化,以确保在main()开始之前,工厂准备好了。这个策略似乎工作时,我动态链接我的库,但不是当我静态链接;当我静态链接时,只有一些我的静态数据成员被初始化。



假设我的工厂生成了Cars。我有CarCreator类,可以实例化一些汽车,但不是所有。我想工厂收集所有这些CarCreator类,以便代码寻找一辆新车可以去工厂,而不必知道谁将做实际的建设。



所以我有



CarTypes.hpp

 枚举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 // make 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){//严重错误!
exit();
}
return car_creator_map [type] - > create_a_car(type);
}

...



然后我会有特定的汽车和特定的汽车创造者:



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());

...



动态链接我的库,MiataCreator :: registered将被初始化,静态链接我的库,它不会被初始化。



有了一个静态构建,要请求Miata, car_creator_map 的miata元素将指向NULL,程序将退出。



有什么特殊的私人静态积分数据成员,他们的初始化将以某种方式跳过?是否只有在使用类时才初始化静态数据成员?我的CarCreator类不在任何头文件中声明;他们完全在.cpp文件内。编译器是否可能内联初始化函数,并以某种方式避免调用MyFactory :: factory_register



有没有更好的解决这个注册问题的解决方案?



这不是一个选项在一个单一的功能列出的CarCreators,每个明确注册工厂,然后保证函数被调用。特别是,我想将几​​个库链接在一起,并在这些单独的库中定义CarCreators,但仍然使用单个工厂来构建它们。



...



这里有一些响应,我预计,但是不解决我的问题:



1)你的单身工厂不是线程安全。



2)当您的CarCreators正在初始化时,您的单例工厂可能未初始化即你有一个静态初始化fiasco)
a)我使用单例类的安全版本通过将单例实例放入一个函数。如果这是一个问题,我应该看到输出如果我添加一个打印语句到 MiataCreator的:: register_with_factory 方法:我不。




不能依赖静态初始化顺序,除非:


  1. 在同一翻译单元(.cpp文件)中定义的静态变量将按列出的顺序初始化

  2. 静态


  3. 您在翻译单元中定义的变量将在第一次调用该翻译单元中的任何函数或方法之前进行初始化。 不能依赖的是,在第一次调用某些其他翻译单元中的函数或方法之前,将初始化静态变量。



    特别是,在第一次调用MyFactory :: create_car(在MyFactory.cpp中定义)之前,您不能依赖于MiataCreator :: registered(在Miata.cpp中定义)进行初始化。



    像所有未定义的行为,有时你会得到你想要的,有时你不会,和最奇怪的最似乎不相关的东西(如静态和动态链接)可以改变是否按照你想要的方式工作。



    你需要做的是为在Miata.cpp中定义的注册标志创建静态访问器方法,让MyFactory工厂通过这个访问器获取值。由于访问器与变量定义在同一翻译单元中,因此变量将在访问器运行时初始化。然后,您需要从某处调用此访问器。


    I'm trying to register a bunch of classes with a factory at load time. My strategy is to harness static initialization to make sure that before main() begins, the factory is ready to go. This strategy seems to work when I link my library dynamically, but not when I link statically; when I link statically, only some of my static data members get initialized.

    Let's say my factory builds Cars. I have CarCreator classes that can instantiate a handful of cars, but not all. I want the factory to collect all of these CarCreator classes so that code looking for a new Car can go to the factory without having to know who will be doing the actual construction.

    So I've got

    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 );
    }
    

    ...

    Then I'll have specific cars and specific car creators:

    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() );
    

    ...

    To reiterate: dynamically linking my libraries, MiataCreator::registered will get initialized, statically linking my libraries, it will not get initialized.

    With a static build, when someone goes to the factory to request a Miata, the miata element of the car_creator_map will point to NULL and the program will exit.

    Is there anything special with private static integral data members that their initialization will be somehow skipped? Are static data members only initialized if the class is used? My CarCreator classes are not declared in any header file; they live entirely within the .cpp file. Is it possible that the compiler is inlining the initialization function and somehow avoiding the call to MyFactory::factory_register?

    Is there a better solution to this registration problem?

    It is not an option to list iall of the CarCreators in a single function, register each one explicitly with the factory, and then to guarantee that the function is called. In particular, I want to link several libraries together and define CarCreators in these separate libraries, but still use a singular factory to construct them.

    ...

    Here are some responses I am anticipating but which do not address my problem:

    1) your singleton Factory isn't thread safe. a) Shouldn't matter, I'm working with only a single thread.

    2) your singleton Factory may be uninitialized when your CarCreators are being initialized (i.e. you've got a static initialization fiasco) a) I'm using a safe version of the singleton class by putting the singleton instance into a function. If this were a problem, I should see output if I added a print statement to the MiataCreator's::register_with_factory method: I don't.

    解决方案

    I think you have a static initialization order fiasco, but not with the Factory.

    It's not that it's the registered flag is not getting initialized, it's just not getting initialized soon enough.

    You cannot rely on static initialization order except to the extent that:

    1. Static variables defined in the same translation unit (.cpp file) will be initialized in the order listed
    2. Static variables defined in a translation unit will be initialized before any function or method in that translation unit is invoked for the first time.

    What you cannot rely on is that a static variable will be initialized before a function or method in some other translation unit is invoked for the first time.

    In particular, you cannot rely on MiataCreator::registered (defined in Miata.cpp) to be initialized before MyFactory::create_car (defined in MyFactory.cpp) is invoked for the first time.

    Like all undefined behavior, sometimes you will get what you want, and sometimes you won't, and the strangest most seemingly-unrelated things (such as static versus dynamic linking) can change whether it works the way you want it to or not.

    What you need to do is create static accessor method for the registered flag that is defined in Miata.cpp, and have the MyFactory factory get the value through this accessor. Since the accessor is in the same translation unit as the variable definition, the variable will be initialized by the time the accessor runs. You then need to call this accessor from somewhere.

    这篇关于为什么静态数据成员不能被初始化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
C/C++开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆