草庐IT

c++ - (静态初始化/模板实例化)工厂模式的问题

coder 2024-02-17 原文

为什么以下代码会引发异常(在调用 map::at 的 createObjects 中) 或者可以查看代码(及其输出)here

有趣的是,如果注释行在 microsoft 和 gcc 编译器(参见 here)中都被取消注释,代码将按预期工作,这甚至可以将 initMap 作为普通静态变量而不是静态 getter 使用。

我能想到的唯一原因是静态 registerHelper_ 对象 (factory_helper_) 和 std::map 对象 (initMap) 是错误的,但是我看不出这是怎么发生的,因为 map 对象是在第一次使用时构造的,而且是在 factory_helper_ 构造函数中构造的,所以一切都应该没问题,不是吗? 更令我惊讶的是,那些 doNothing() 行解决了这个问题,因为对 doNothing() 的调用无论如何都会在关键部分(当前失败)通过之后发生。

编辑: 调试显示,如果不调用 factory_helper_.doNothing(),则永远不会调用 factory_helper_ 的构造函数。

#include <iostream>
#include <string>
#include <map>

#define FACTORY_CLASS(classtype) \
extern const char classtype##_name_[] = #classtype; \
class classtype : FactoryBase<classtype,classtype##_name_>

namespace detail_
{
    class registerHelperBase
    {
    public:
        registerHelperBase(){}
    protected:
        static std::map<std::string, void * (*)(void)>& getInitMap() {
            static std::map<std::string, void * (*)(void)>* initMap = 0;
            if(!initMap)
                initMap= new std::map<std::string, void * (*)(void)>();
            return *initMap;
        }
    };

    template<class TParent, const char* ClassName>
    class registerHelper_ : registerHelperBase {
        static registerHelper_ help_;
    public:
        //void doNothing(){}
        registerHelper_(){
            getInitMap()[std::string(ClassName)]=&TParent::factory_init_;
        }
    };
    template<class TParent, const char* ClassName>
    registerHelper_<TParent,ClassName> registerHelper_<TParent,ClassName>::help_;
}

class Factory : detail_::registerHelperBase
{
private:
    Factory();
public:
    static void* createObject(const std::string& objclassname) {
        return getInitMap().at(objclassname)();
    }
};


template <class TClass, const char* ClassName>
class FactoryBase {
    private:
        static detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> factory_helper_;
        static void* factory_init_(){ return new TClass();}
    public:
        friend class detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName>;
        FactoryBase(){
            //factory_helper_.doNothing();
        }
        virtual ~FactoryBase(){};
};

template <class TClass, const char* ClassName>
detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> FactoryBase<TClass,ClassName>::factory_helper_;


FACTORY_CLASS(Test) {
public:
    Test(){}
};

int main(int argc, char** argv) {
    try {
        Test* test = (Test*) Factory::createObject("Test");
    }
    catch(const std::exception& ex) {
        std::cerr << "caught std::exception: "<< ex.what() << std::endl;
    }
    #ifdef _MSC_VER
        system("pause");
    #endif
    return 0;
}

最佳答案

问题与初始化顺序无关,而是与模板实例化有关。

模板化代码是按需实例化的,也就是说,编译器不会实例化您的程序中未使用的任何模板化代码。特别是,在您的情况下,静态类成员 FactoryBase<>::factory_helper_没有被实例化,因此它不存在于最终的二进制文件中,它没有注册自己......(你可以用 gnu 工具链中的'nm'检查它,这将显示你的可执行文件中存在的符号列表)

尝试更改 FactoryBase构造函数:

template <class TClass, const char* ClassName>
class FactoryBase {
   //...
   FactoryBase(){
      factory_helper_;
   }
   //...
};

这将强制编译器实际实例化二进制文件中的静态成员,您应该被设置。无需创建空方法并调用它。

编辑:作为对评论的回答,在当前标准的 §14.7.1[temp.inst]/1 段末尾:

Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

关于c++ - (静态初始化/模板实例化)工厂模式的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2851991/

有关c++ - (静态初始化/模板实例化)工厂模式的问题的更多相关文章

  1. 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”因此,为了解决

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

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

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  5. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  6. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  7. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  8. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  9. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  10. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

随机推荐