当对象是模板时静态构造

时间:2013-09-05 01:49:42

标签: c++ gcc mingw32

我有一个工厂类,我已经使用了很长一段时间,我试图做一些模板魔术,它根本不起作用。我不认为你需要看到那个类,因为它非常大,但如果你真的认为它是相关的,那么我将编辑它。无论如何,我有一个这样的设置,我已经永远做了。

class Base :
    public Factory<Base>
{
};

class Derived :
    public Base
{
    static Factory<Base>::DerivedRegister<Derived> m_Reg;
};

Factory<Base>::DerivedRegister<Derived> Derived::m_Reg;

工厂为它提供静态方法createInstance(const std :: string&amp; name),它将返回已注册对象的实例。 DerivedRegister对象派生自工厂对象,并且在静态声明时,必须在静态初始化期间构造。这意味着我可以在其构造函数中访问工厂静态内部,因此我利用它的构造函数在静态初始化期间将所有注册对象提供给工厂。这工作并且有一段时间了,但后来我尝试了这样的事情,以便派生类可以避免需要显式地创建静态的derivedregister对象:

class Base :
    public Factory<Base>
{
};

template<class TDerived>
class BaseReg :
    public Base
{
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};

template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;

class Derived :
    public BaseReg<Derived>
{
};

当我运行程序并尝试创建Derived类的实例时,它在工厂中不存在,经过一点涂鸦后,我发现静态DerivedRegister永远不会在派生中构造,就像在永远不会调用构造函数。我发现这很奇怪。如果Derived类不是模板,怎么能不强制静态初始化?当我创建一个虚拟静态对象时,如下所示,并使用m_Reg中的方法进行静态构造,突然DerivedRegister构造正确,工厂可以实例化它,没有汗水。

class Base :
    public Factory<Base>
{
};

template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};

template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;

class Derived :
    public BaseReg<Derived>
{
    class RandomClass
    {
    public:
         RandomClass(std::string meh) {}
    };

private:
    static RandomClass m_Obj;
};

Derived::RandomClass Derived::m_Obj(m_Reg.func());

那么我在这里没有得到关于这个模板类的静态成员初始化的内容?是不是必须像任何其他非模板对象一样静态初始化对象?

编辑:mkay,为了提供一点见解,我将发布Factory对象。只要注意文本的WALLS。忽略额外的包含,多余的GetNames和func函数,它们只是voodoo代码。

#ifndef FACTORY_H
#define FACTORY_H

// library tools
#include <map>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <cxxabi.h>

const std::string demangle(const char* name);

template<class base>
class Factory
{
protected:
    template<class T>
    static base * createT() { return new T;}

    typedef std::map<std::string, base*(*)()> map_type;

    static map_type& GetMap()
    {
        static map_type map;
        return map;
    }

public:
    virtual ~Factory(){}

    static base * createInstance(const std::string & s)
    {
        if(!GetMap().count(s))
            return nullptr;
        typename map_type::iterator it = GetMap().find(s);
        return it->second();
    }

    template <class TDerived>
    struct DerivedRegister :
        public Factory<base>
    {
        DerivedRegister()
        {
            std::string name = demangle(typeid(TDerived).name());
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }

        DerivedRegister(const std::string& name)
        {
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }

        std::string func() {return "meh";}
    };

    static void GetNames(std::vector<std::string>& names)
    {
        names.clear();

        for(auto it = GetMap().begin(); it != GetMap().end(); ++it)
            names.push_back(it->first);

    }
};


#endif

1 个答案:

答案 0 :(得分:2)

你可能在这里遇到的是不可预测的静态初始化顺序的困境,假设你是静态地构建工厂本身,或者它是作为其他静态结构链的一部分构建的。

你可以通过在自己的函数中包装和使用静态来解决这个问题:

template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> & Reg()
    {
        static Factory<Base>::DerivedRegister<TDerived> m_Reg;
        return m_Reg;
    }
};

现在,访问m_Reg的唯一方法是致电Reg(),之后您可以确定m_Reg已构建完毕。也就是说,它是在第一次使用它的任何时候构建的。

我已经用上述方法成功修复了一些非常毛茸茸的问题,现在我使用它当然是为了避免调试奇怪的崩溃。我几乎不再需要静态成员变量了。