std::map::insert 处的 C++ 分段错误

C++ Segmentation fault at std::map::insert

我正在尝试自学 C++(实际上我应该说 re-learn,但我第一次学习它是在一年前我对编码一窍不通的时候所以它不算数)和完成在线教程后,我正在做我的第一个项目。我想既然我有很好的 C# 和 VB.Net 背景,我不妨尝试一些更大的东西,但不要太大。在我开始之前,我使用 Code::Blocks 作为我的 IDE 和 IDE 中的默认编译器(我相信它是 MinGW)。所以这是我的事情:我有一个 ChromaTest 项目(对于那些想知道名称的人,这是使用 Razer Chroma SDK),它是一个控制台应用程序,还有一个 ChromaTestDLL 项目,它是(你猜对了)一个 DLL(我决定做一个 DLL 来学习如何同时这样做,因为我稍后可能会在 GUI 项目中使用一些代码)。问题是我在尝试插入地图时遇到分段错误。这是相关代码:

在 ChromaTestDLL 项目中

MyChroma.h(Header MyChroma class)

#ifndef MYCHROMA_H
#define MYCHROMA_H

#include <map>
#include <windef.h>
#include "RzChromaSDKTypes.h"
#include <string>
#include "Template.h"

#ifdef BUILD_DLL
    #define DLL_EXPORT __declspec(dllexport)
#else
    #define DLL_EXPORT __declspec(dllimport)
#endif

using namespace std;

#ifdef __cplusplus
extern "C"
{
#endif

class DLL_EXPORT MyChroma
{
    public:
        MyChroma();

        bool Init();

        std::map<char, COLORREF> GetColorMapping();

        void SetColorMapping(char key, COLORREF color);

        void AssignToKeyBoard();

        void SetColorFromString(string s, COLORREF color);

        ~MyChroma();

    protected:
        std::map<char, COLORREF>* _ColorMapping;

        ChromaSDK::Keyboard::RZKEY KeyFromChar(char keyChar);

        My_Chroma_Implementation* Chroma;

    private:
};

#ifdef __cplusplus
}
#endif

#endif // MYCHROMA_H

MyChroma.cpp(MyChroma class的相关实现)

#include "MyChroma.h"
#include "Template.h"
#include <iostream>

MyChroma::MyChroma()
{
     _ColorMapping = new std::map<char, COLORREF>();
}

std::map<char, COLORREF> MyChroma::GetColorMapping() { return *_ColorMapping; }

void MyChroma::SetColorMapping(char key, COLORREF color){

    if (_ColorMapping->count(key) == 0)
        _ColorMapping->insert(std::make_pair(key, color)); //This where the error happens
    else
        (*_ColorMapping)[key] = color;
}

MyChroma::~MyChroma() {
    delete Chroma;
    delete _ColorMapping;
}

//Other implementations omitted

在 ChromaTest 项目中

MyChroma.h(Header导入MyChromaclass,和ChromaTestDll里的略有不同,基本上只包含public 成员)

#ifndef MYCHROMA_H
#define MYCHROMA_H

#include <map>
#include <windef.h>
#include <string>

#ifdef BUILD_DLL
    #define DLL_EXPORT __declspec(dllexport)
#else
    #define DLL_EXPORT __declspec(dllimport)
#endif

using namespace std;

#ifdef __cplusplus
extern "C"
{
#endif

class DLL_EXPORT MyChroma
{
    public:
        MyChroma();

        bool Init();

        std::map<char, COLORREF> GetColorMapping();

        void SetColorMapping(char key, COLORREF color);

        void AssignToKeyBoard();

        void SetColorFromString(string s, COLORREF color);

        ~MyChroma();

};

#ifdef __cplusplus
}
#endif

#endif // MYCHROMA_H

Main.cpp(主应用程序代码)

#include <iostream>
#include "MyChroma.h"

#include <wingdi.h>

using namespace std;

int main()
{
    MyChroma test = MyChroma();
    bool result = test.Init();

    cout << (result ? "Initialized\n" : "Failed to initialize Razer Chroma");

    cout << "Setting color";

    if (result){
        test.SetColorMapping('a', RGB(255,0, 0)); //This call goes in the DLL where I said it failed earlier.
        test.SetColorMapping('a', RGB(0,0,255));
    }

    return 0;
}

抱歉,代码太长了(请告诉我这里是否有我可以删除的内容)。任何人都可以发现其中的任何错误我不会感到惊讶这将链接到指针,这可能是我花了最多时间来理解的概念。起初我没有将地图放在指针和堆上,但将另一个变量更改为之前的变量似乎解决了另一个问题,所以我想我会试一试。遗憾的是,当我没有将地图也放在堆上时,我也遇到了几乎相同的错误。

附带一提,谁能向我解释一下堆和堆栈之间的区别,为什么我要经历在堆上存储变量的(有风险的)麻烦(使用指针和删除等等)而不是在堆栈上,什么时候应该使用堆,什么时候不应该。

根据您问题中的信息:

  1. 您的 DLL 中的编译代码似乎在其头文件中声明了一个 MyChroma class 包含一堆内部 class 成员。

  2. 然后你的主应用程序使用一个完全不同的头文件,它定义了一个名为 MyChroma 的 class,去掉了它的 class 成员。

  3. 然后,您的主应用程序根据它在其头文件中看到的内容实例化 MyChromaclass。

那是行不通的。由于您的主应用程序对那些 class 成员一无所知,因此它实例化的实际 class 太小了。

并在堆栈上实例化一个 class。

然后构造函数来自 DLL,它认为 class 包含所有其他 class 成员。

并且 DLL 中的构造函数尝试初始化它们。

在堆栈上。

你好堆栈损坏。

这里的答案很简单"don't do what you did"。这是未定义的行为。您编译的所有引用特定 class 的内容都必须看到 class.

的相同声明(和内联方法定义)

句号。

没有例外。

好吧,如果有足够的经验,在针对特定的 C++ 实现时,可以安全地执行类似的操作,但这里不是这种情况。

在那之前,有一些方法可以隐藏库提供的 classes 的内部实现细节,但这不是您的做法。安全的方法是使用 PIMPL design pattern.

还有一些您不应该做的事情。这与手头的问题没有直接关系,但这将避免其他几个常见的陷阱,这些陷阱可能会在没有事先警告的情况下将地毯从脚下拉下来:

  1. 不要使用use namespace std;。特别是在头文件中。完全忘记在C++语言中存在类似的东西。

  2. 你所有的 classes 也应该 follow the Rule Of Three.