[英]std::map::insert exception
這是另一個,“我的代碼不工作,我不知道為什么,”我擔心的問題。 我只是沒有足夠的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 *指向。
例:
啊
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;
BH
#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:訪問沖突讀取位置0x00000004。
所以實際上,問題歸結為,std :: map :: insert何時拋出異常以及原因。 我只知道每個人都會問我正在做什么的背景。 很低,看到一個巨大的文字牆出現了! 我真正需要的只是預感。
也應該我或者不應該標記d3d11,因為這個問題與它沒有關系?
我的猜測是,這是由於靜態變量的初始化順序。 無法控制此訂單。 所以你無法保證你的初始化:
Factory<A>::map_type Factory<A>::m_Map;
在此初始化之前調用:
DEFINE_DERIVED(A, B, "B")
在這種情況下,后一個語句必須首先進行初始化,因此您尚未分配映射。
另一種設計模式將控制單件工廠的初始化。 如果你在每個創建工廠對象上都有一個顯式的初始化函數,那么你可以在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;
如果您有很多工廠,您可能需要一個初始化函數來初始化它們,並且您必須記住在創建它們時添加新工廠。
這是一個問題:
std::map<std::string, base*(*)()>::iterator it = m_Map.find(s);
return it->second();
如果對find
的調用失敗(即,它無法在地圖中找到's'),那么它將返回m_Map.end()
。 解除引用這是禁忌。
好吧,我實際上已經為這個錯誤做了大約一天的工作,現在才知道出了什么問題。
問題1:
衍生的着色器標頭從未實際包含在整個項目的任何地方,盡管事實上它永遠不需要直接實例化,但它仍然必須包含在某處,以便它可以鏈接並包含在構建中。
問題2:
有趣的是,就像組合說的那樣,初始化順序不是一個接一個地完成,而是查看我的舊代碼,它似乎在之前正確初始化。 這里的區別是,我將派生對象的工廠放在一個不同的對象中,然后是基類。 我以前做的是在基類中聲明一個靜態函數和靜態工廠,以便您可以從基類本身實例化它的任何已注冊的派生類。 當工廠包含在基類中時,實例化是通過靜態函數完成的,所有靜態的初始化順序似乎是按順序(不確定這是否總是如此)。 改變之后它現在運行正常。
所以現在,我的回答是,你可以獲得這樣的操作系統異常,試圖使用對項目中任何地方從未實際包含的對象的引用。 我沒有很好的編譯器或鏈接器的知識告訴你為什么它似乎編譯好,盡管這個對象永遠不會被包括在內。 如果有人想延長我的答案,請。
如果涉及到這種困境,我會使用MSVC ++ 2010 express。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.