jjzjj

c++ - 为什么静态数据成员可能没有被初始化?

coder 2024-02-01 原文

我试图在加载时向工厂注册一堆类。我的策略是利用静态初始化来确保在 main() 开始之前,工厂已准备就绪。当我动态链接我的库时,这种策略似乎有效,但当我静态链接时无效;当我静态链接时,只有我的一些静态数据成员被初始化。

假设我的工厂生产汽车。我有 CarCreator 类可以实例化少数汽车,但不是全部。我希望工厂收集所有这些 CarCreator 类,以便寻找新 Car 的代码可以去工厂,而不必知道谁将进行实际构建。

所以我有

汽车类型.hpp

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

我的工厂.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;
};

我的工厂.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 );
}

...

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

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,程序将退出。

私有(private)静态整数数据成员有什么特别之处,它们的初始化会以某种方式被跳过吗?是否仅在使用该类时才初始化静态数据成员?我的 CarCreator 类没有在任何头文件中声明;它们完全存在于 .cpp 文件中。编译器是否可能正在内联初始化函数并以某种方式避免调用 MyFactory::factory_register

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

在单个函数中列出所有 CarCreators,向工厂显式注册每个 CarCreators,然后保证函数被调用,这不是一种选择。特别是,我想将几​​个库链接在一起,并在这些单独的库中定义 CarCreator,但仍使用单一工厂来构造它们。

...

以下是一些我期待但没有解决我的问题的回复:

1) 你的单例工厂不是线程安全的。 a) 没关系,我只使用一个线程。

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

最佳答案

我认为您有一个静态初始化顺序失败,但工厂没有。

并不是注册标志没有被初始化,只是没有足够快地被初始化。

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

  1. 在同一翻译单元(.cpp 文件)中定义的静态变量将按照列出的顺序进行初始化
  2. 翻译单元中定义的静态变量将在首次调用该翻译单元中的任何函数或方法之前进行初始化。

不能依赖的是,在首次调用其他翻译单元中的函数或方法之前初始化静态变量。

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

像所有未定义的行为一样,有时你会得到你想要的,有时你不会,最奇怪的最看似无关的事情(例如静态链接与动态链接)可以改变它是否按照你想要的方式工作还是不是。

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

关于c++ - 为什么静态数据成员可能没有被初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1300836/

有关c++ - 为什么静态数据成员可能没有被初始化?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  4. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  9. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  10. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

随机推荐