簡體   English   中英

std :: map ::插入異常

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM