在std::map::insert时出现分段错误

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

本文关键字:分段 错误 insert std map      更新时间:2023-10-16

我正在尝试自学c++(实际上我应该说重新学习,但我第一次学习它是在我对编码一无所知的时候,一年前,所以它不算数),我在完成在线教程后正在做我的第一个项目。我认为,因为我有一个很好的c#和VB。Net背景,我不妨尝试一些大一点,但不是太大。在我开始之前,我使用Code::Blocks作为我的IDE和IDE中的默认编译器(我相信它是MinGW)。所以这是我的事情:我有一个chroest项目(这是使用雷蛇Chroma SDK对于那些想知道名字的人),这是一个控制台应用程序,和一个chroestdll项目,这是(你猜对了)一个DLL(我决定做一个DLL来学习如何在同一时间这样做,因为我可能会在GUI项目中使用一些代码)。问题是我得到一个分割错误时,试图插入到一个地图。以下是相关代码:

在chrostestdll项目

MyChroma.h (MyChroma类的头文件)

#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类的相关实现)

#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

MyChroma.h(导入MyChroma类的头,与chroestdll中的头略有不同,基本上它只包含公共成员)

#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 ? "Initializedn" : "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类。

  2. 那么你的主应用程序使用一个完全不同的头文件,它定义了一个叫做MyChroma的类,剥离了它的类成员。

  3. 然后,你的主应用程序实例化MyChroma类,基于它在头文件中看到的内容。

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

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

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

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

在堆栈上。

Hello栈损坏。

这里的答案很简单:"不要做你做过的事"。这是未定义的行为。编译引用特定类的所有内容都必须看到该类的相同声明(和内联方法定义)。

句号。

没有例外。

嗯,有了足够的经验,在针对特定的c++实现时,可以安全地做这样的事情,但这里的情况并非如此。

在此之前,有很多方法可以隐藏库提供的类的内部实现细节,但这不是您要做的。安全的方法是使用PIMPL设计模式。

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

  1. 不要使用use namespace std;尤其是在头文件中。完全忘记c++语言中存在这样的东西。