1

我有一个工厂类,我已经使用了很长一段时间,我试图对其进行一些模板魔术,但它根本行不通。我认为您不需要查看该课程,因为它很大,但是如果您真的认为它相关,那么我将对其进行编辑。无论如何,我有一个这样的设置,我已经永远做了。

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& name),它将返回已注册对象的实例。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 对象。请注意文字的墙壁。忽略额外的包含,以及多余的 GetNames 和 func 函数,它们只是巫毒代码。

#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
4

1 回答 1

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已构建。也就是说,它是在第一次使用时构建的。

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

于 2013-09-05T02:02:33.063 回答