2012-09-27 113 views
1

这是另一个,“我的代码不工作,我不知道为什么,”我害怕的问题。我只是没有足够的知识stl知道为什么std :: map :: insert会引发异常。如果你知道什么情况会引发异常,你可以跳过这段文字并回答。如果你迫切需要关于这个问题的一些背景知识,那么就对它有所了解。我会发布我的代码并解释所做的事情,如果您对stl有更深入的了解,可以解释我插入的电话会出现什么问题,我将非常感激。std :: map :: insert exception

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

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。ħ

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构造函数,特别是在插入调用时。异常错误是这样的:0000005:访问 冲突读取位置0x00000004

在0x772315de在Syncopate.exe未处理的异常。

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

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

+1

我没有阅读整篇文章,但访问冲突通常意味着取消引用空指针 – shengy

+0

@shengy - yup,但事情是,工厂内的std :: map不是指针。我从来没有创建一个新的地图或删除它:\。所以什么时候它会产生一个异常,map不是一个指针?如果未定义,它会引发编译错误:\。 – FatalCatharsis

+0

它不会引发异常。在代码的任何地方都没有'throw',无论是你的还是图书馆。运行时系统(操作系统,用于所有实际目的)都想告诉程序它是borken,并且这有一个例外的形式。在某个地方你有一个糟糕的指针(数据损坏),并且这可能导致访问违规,可能会或可能不会接近腐败点。 –

回答

1

好吧,我实际上一直在努力解决这个错误大约一天,直到现在我才意识到什么是错的。

问题1:

派生的着色器头从来没有真正纳入整个项目的任何地方,尽管它永远不需要直接实例化的事实,但它仍然在某处被包括在内,以便它可以进行链接包含在构建中。

问题2:

足够有趣,就像组合说,初始化顺序没有做一前一后,但后来找过我的旧代码,它似乎之前正确初始化。这里的区别是什么,我把派生对象的工厂放在一个不同的对象中,然后是基类。我曾经做过的事情是在基类中声明一个静态函数和静态工厂,以便可以从基类本身实例化它的任何注册派生类。当工厂被包含在基类中时,而实例化是通过一个静态函数完成的,所有静态的初始化顺序似乎是有序的(不确定这是否始终为真)。改变它之后它现在运行正常。

所以现在,我的答案是,您可以获取像这样的操作系统异常,以便尝试使用对项目中实际从未实际包含的对象的引用。我没有很好的编译器或链接器知识来告诉你它为什么编译好,尽管这个对象从未包含在内。如果有人想延长我的回答,请。

我使用MSVC++ 2010表达如果属于这种困境。

1

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

Factory<A>::map_type Factory<A>::m_Map; 

这个初始化之前被调用:

DEFINE_DERIVED(A, B, "B") 

在这种情况下,后者的说法一定是首先初始化,所以你映射尚未分配。

另一种设计模式可以控制单件工厂的初始化。如果每个创建工厂对象都有一个显式的Initialize函数,那么您可以在main的开始处调用它。例如。

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

这里有一个问题

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

如果调用find失败(I,E它无法找到的“中图),那么它将返回m_Map.end()。解引用是一个禁忌。

+0

这是真的,这就是为什么if语句在检查它是否有索引's',确保调用时find不会失败。 – FatalCatharsis

+0

错过了;我的错。 –

+0

没有probs,它有很多代码需要通过:\ – FatalCatharsis