std :: map ::插入异常

时间:2012-09-27 01:57:23

标签: c++ stl

这是另一个,“我的代码不工作,我不知道为什么,”我担心的问题。我只是没有足够的stl知识来知道为什么std :: map :: insert会引发异常。如果您知道它抛出异常的情况,您可以跳过这一段文本并回答。如果你只是迫切需要一些关于这个问题的背景知识,那就去做吧。我将发布我的代码并解释已完成的工作,如果你对stl有更好的了解,你能解释我的插入调用可能出错的地方,我将非常感激。

我前一段时间写过一个对象,我偶尔会用它去工厂对象。它的主要目的是基本上取一个字符串并存储字符串和“创建新对象函数”指针,这样最后,你可以调用一个函数,传递一个字符串,如果有一个有效的注册,它返回派生对象的新实例。少说话,更多代码,这就是我得到的:

factory.h

#ifndef FACTORY_H
#define FACTORY_H

// library tools
#include <map>
#include <string>

// Simplified registration macros
#define DECLARE_DERIVED(T, base)    static Factory<base>::DerivedRegister<T> reg;
#define DEFINE_DERIVED(T, base, s)  Factory<base>::DerivedRegister<T> T::reg(s); 

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

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

    virtual ~Factory(){ }

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

    template <class T>
    struct DerivedRegister;

protected:
    static map_type m_Map; 
};

template<class base>
template<class T>
struct Factory<base>::DerivedRegister : public Factory<base>
{
    DerivedRegister(std::string const & s)
    {
        m_Map.insert(std::pair<std::string, base*(*)()>(s, &createT<T>));
    }
};


#endif

这里有一个更好的解释,它真正做到了什么。假设你有一个基类,A类。然后你有任意数量的派生类。我将一个工厂对象设置为模板化的A,然后手动创建派生的寄存器对象,或者使用派生类声明中顶部的宏来创建静态注册表对象。然后在实现中定义它并调用它的构造函数,传入一个字符串以用于标识对象。使用工厂成员createInstance,您可以传入一个字符串标识符,并返回一个派生对象,由A *指向。

示例:

A.H

class A
{

};

A.cpp

// the map for this factory template has to be defined somewhere, as it is static
Factory<A>::map_type Factory<A>::m_Map;

b.h

#include <A.h>
class B : public A
{
  // anywhere in declaration of derived B
  DECLARE_DERIVED(A, B)
};

b.cpp

 // just somewhere in cpp file
 DEFINE_DERIVED(A, B, "B")

的main.cpp

int main()
{
  A * ptr;
  Factory<A> factory;

  ptr = factory.createInstance("B");
}

这个对象过去对我有用,大多数都没有障碍。现在,我正在做一个更复杂的项目。我喜欢与游戏引擎有关的数据组织/ api设计,我只是想实现一个编目器(但不是实例化的)着色器的解决方案,这样你就可以获得整个着色器列表了。已编程,但除非需要,否则它们不会在运行时实例化。除此之外,这个问题实际上与d3d11无关,或者至少我不希望如此。

所以这是正在发生的事情。我有一个表示图形着色器抽象类的对象。您希望编写的所有着色器必须从此对象派生。您可以从不同的着色器中获得并实现它的功能。

让我们在命名空间同步和派生着色器“ColorShader”“LightShader”和“TextureShader”中调用基础对象“SYNC :: D3D11Shader”。因为我不想简单地想要在渲染对象中创建这些着色器实例的std :: map,所以我在渲染对象中创建了一个像这样的工厂。

D3D11Renderer.h

class D3D11Renderer
{
    // many other members...
    Factory<D3D11Shader> m_ShaderFactory;
    // many other member...
};

D3D11Renderer.cpp

// define this templated classes map or you'll get undefined errors
Factory<SYNC::D3D11Shader>::map_type Factory<SYNC::D3D11Shader>::m_Map;

然后在ColorShader中我使用像这样的宏

D3D11ColorShader.h

class D3D11ColorShader : public SYNC::D3D11Shader
{
  // ...lotsa members
  DECLARE_DERIVED(D3D11ColorShader, SYNC::D3D11Shader)
  // lotsa member...
};

D3D11ColorShader.cpp

// define the registery object with it's key here
DEFINE_DERIVED(D3D11ColorShader, SYNC::D3D11Shader, "ColorShader")

这一切都编译得很好,并且抛出它的例外情况是我首先在D3D11ColorShader.cpp中调用registryObjects构造函数,特别是在插入调用时。异常错误是这样的:

  

Syncopate.exe中0x772315de处的未处理异常:0xC0000005:Access   违规阅读地点0x00000004。

所以实际上,问题归结为,std :: map :: insert何时抛出异常以及为什么。我只知道每个人都会问我正在做什么的背景。很低,看到一个巨大的文字墙出现了!我真正需要的就是预感。

也应该我或者不应该标记d3d11,因为这个问题并不真正属于它?

3 个答案:

答案 0 :(得分:1)

我的猜测是,这是由于静态变量的初始化顺序。无法控制此订单。所以你无法保证你的初始化:

Factory<A>::map_type Factory<A>::m_Map;
在初始化之前调用

DEFINE_DERIVED(A, B, "B")

在这种情况下,后面的语句必须首先被初始化,因此你没有分配map。

另一种设计模式将控制单件工厂的初始化。如果你在每个创建工厂对象上都有一个显式的初始化函数,那么你可以在main的开头调用它。 E.g。

Factory.h

class Factory {
private:
    static Factory* instance_;
public:
    static Initialize(){instance_=new Factory;}
    Factory* instance(){return instance_;}
}

Factory.cpp

static Factory* Factory::instance_ = NULL;

如果您有很多工厂,您可能需要一个初始化函数来初始化它们,并且您必须记住在创建它们时添加新工厂。

答案 1 :(得分:1)

这是 a 问题:

   std::map<std::string, base*(*)()>::iterator it = m_Map.find(s);
   return it->second();

如果对find的调用失败(即,它无法在地图中找到's'),则会返回m_Map.end()。取消引用这是禁忌。

答案 2 :(得分:1)

好吧,我实际上已经为这个错误做了大约一天的努力,现在才知道出了什么问题。

问题1:

派生的着色器标头从未实际包含在整个项目的任何地方,尽管事实上它永远不需要直接实例化,但它仍然必须包含在某处,以便它可以链接并包含在构建中。

问题2:

有趣的是,就像组合所说的那样,初始化顺序并没有一个接一个地完成,但随后查看我的旧代码,它似乎在之前正确初始化。这里的区别是,我将派生对象的工厂放在一个不同的对象中,然后是基类。我以前做的是在基类中声明一个静态函数和静态工厂,以便您可以从基类本身实例化它的任何已注册的派生类。当工厂包含在基类中时,实例化是通过静态函数完成的,所有静态的初始化顺序似乎是按顺序(不确定这是否总是如此)。更改后,它现在运行正常。

现在,我的回答是,你可以获得这样的操作系统异常,试图使用对项目中任何地方从未实际包含的对象的引用。我没有很好的编译器或链接器的知识告诉你为什么它似乎编译好,尽管这个对象永远不会被包括在内。如果有人想延长我的答案,请。

如果属于这种困境,我会使用MSVC ++ 2010 express。