【问题标题】:C++: std::map<int,int> crashes when being assignedC++:分配时 std::map<int,int> 崩溃
【发布时间】:2014-01-12 10:01:49
【问题描述】:

我正在尝试创建一个 int 到 int 的映射,但程序崩溃了,我不知道为什么。 我已经将问题总结为这个简短的简单代码。

当程序启动时,由于 Utilities 成员 _instance 是静态的 - 它通过转到其构造函数来对其进行初始化,该构造函数包含一行:一个 int 到 int(简单)映射分配。但随后它崩溃了。

请注意,如果我注释该行,程序不会崩溃, 并且 main 包含相同的行。 所以我的两个问题是:

1) 为什么会崩溃?这种行为背后有什么意义吗?

2) 如何修复它以便在构造函数中初始化地图?

谢谢

#include <map>

class Utilities
{
public:

    ~Utilities(){};
    static Utilities& instance();

private:
    Utilities();
    Utilities( const Utilities& ){};

    static Utilities _instance;
    static std::map<int, int> textIntToIntMap;
};


Utilities Utilities::_instance = Utilities();
std::map<int, int> Utilities::textIntToIntMap;

Utilities::Utilities()
{
    //The following line crashes, why?
    textIntToIntMap[1] = 2;
}

int main()
{
    static std::map<int, int> text2;
    text2[4] = 2;
    int xxx = 3;
}

【问题讨论】:

  • 为单例类定义复制构造函数真的没有意义。如果有的话,您只想声明它,而不是定义它,以明确防止复制。

标签: c++


【解决方案1】:

你有一个初始化顺序问题:

Utilities Utilities::_instance = Utilities();

此行调用Utilities 的默认构造函数,然后尝试填充地图。但是地图在这个阶段没有初始化。

您应该将您的代码设计为能够应对此类初始化顺序问题。您可以通过在函数中创建静态实例来缓解其中的一些问题。这使您可以处理初始化的顺序。但是对定义进行简单的重新排序应该可以解决当前的问题:

std::map<int, int> Utilities::textIntToIntMap;
Utilities Utilities::_instance = Utilities();  // OK, map has been defined

【讨论】:

    【解决方案2】:

    问题是您在构造 Utilities::textIntToIntMap 之前调用了 Utilities 构造函数。

    交换以下两行的顺序:

    Utilities Utilities::_instance = Utilities();
    std::map<int, int> Utilities::textIntToIntMap;
    

    【讨论】:

      【解决方案3】:

      在初始化 _instance 之前,您需要在类外部定义映射 textIntToIntMap。因为在构造函数中你使用的是 textIntToIntMap 这是一个静态成员,所以你需要先定义它。所以在课堂之外使用以下几行:

      std::map<int, int> Utilities::textIntToIntMap;
      Utilities Utilities::_instance = Utilities();
      

      【讨论】:

        【解决方案4】:

        其他人已经发现Utilities() 是在Utilities::textToIntMap 之前构造的。那么,现在的问题是:以后如何避免此类问题?

        您可以使用返回对静态局部变量的引用的函数来确保在使用引用之前完成构造。您可以将此类函数放入命名空间中。您还应该有一个方便的 typedef,希望名称更短,这样在 C++98 上声明迭代器就不是一个麻烦事。在 C++11 上,无论如何你都应该使用 auto

        请注意,使用全局或函数静态非 POD 数据在 C++98 中不是线程安全的。如果您希望从多个线程安全地使用textToMap(),但不能保证在第二个线程启动之前访问它,textToIntMap 需要将初始化包装在互斥体中。要大致了解如何做到这一点,请参阅 Qt 方便的 Q_GLOBAL_STATIC 中的 inner function

        在这种情况下,使用单例类似乎是一种毫无意义的 Java 主义。

        Run a test on ideone.

        // Utilities.h
        namespace Utilities {
          typedef std::map<int, int> Map;
          Map & textToIntMap();
        }
        
        // Utilities.cpp
        namespace Utilities {
          namespace {
            struct InitializedMap : Map {
              InitializedMap() {
                 insert(value_type(1, 2));
                 // or
                 (*this)[1] = 2;
              }
            };
          }
          Map & textToIntMap() {
            static InitializedMap map;
            return map;
          }
        }
        

        【讨论】:

          【解决方案5】:

          试试这个方法::

          #include <map>
          #include <iostream>
          using namespace std;
          
          class Utilities
          {
          public:
              static Utilities& instance() {
                  static Utilities instance;
                  return instance;
              }
              ~Utilities(){};
              void PrintMapValues();
              void AddKeyValue(int key, int value);
          
          private:
              Utilities();
              Utilities( const Utilities& ){};
          
              std::map<int, int> int_to_int_map_;
          };
          
          Utilities::Utilities()
          {
              //The following line crashes, why?
              int_to_int_map_[-99] = 2;
          }
          
          void Utilities::PrintMapValues() {
              for(std::map<int, int>::iterator it = int_to_int_map_.begin(); it != int_to_int_map_.end(); ++it){
                  cout << "Key:" << it->first << " Val:" << it->second << endl;
              }
          }
          
          void Utilities::AddKeyValue(int key, int value) {
              int_to_int_map_[key] = value;
          }
          
          int main()
          {
              Utilities& utils = Utilities::instance();
          
              for (int i=0; i< 10; i++) {
                  utils.AddKeyValue(i, i+20);
              }
          
              utils.PrintMapValues();
          
              return 0;
          }
          

          【讨论】:

          • 在回答问题时,最好在代码sn-p之外提供一些解释。
          猜你喜欢
          • 2019-10-16
          • 1970-01-01
          • 2010-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-08
          • 1970-01-01
          • 2010-09-13
          相关资源
          最近更新 更多