【问题标题】:Access Violation with local static std::map<std::string, std::weak_ptr> in member function (C++11/STL)成员函数中的本地静态 std::map<std::string, std::weak_ptr> 访问冲突 (C++11/STL)
【发布时间】:2013-09-28 17:51:36
【问题描述】:

我的问题有点复杂,但我尽量简单地描述它。 我想在我的一个类中使用本地静态成员(std::map&lt;std::string, std::weak_ptr&gt; 类型)实现一个静态方法。每次调用此方法时,它都应该查找映射中是否存在以传递的参数为键的对象。该方法返回的值是std::shared_ptr(从映射中的std::weak_ptr 构造,如果std::weak_ptr 可以锁定 - 否则构造一个新的std::shared_ptr 并作为std::weak_ptr 添加到@ 987654328@)。但我在拨打std::map.find() 的线路上“有时”收到访问冲突。

“有时”的意思是:如果一个std::weak_ptr 被添加到地图中,然后因为它无法被锁定而被删除 - 并构造一个新的std::shared_ptr,作为std::weak_ptr 添加到std::map。 下次我的静态方法尝试在 std::map 中查找时,可能(偶尔)会出现访问冲突来自:

File: Microsoft Visual Studio 11.0\VC\include\xtree
Line: 2092
Method: _Nodeptr _Lbound(const key_type& _Keyval)
Access Violation at reading: '_Nodeptr _Pnode = _Root();'

我找不到任何方法来更好地调试问题 - 非常感谢这里的任何帮助。

最后但并非最不重要的一点是,我重写了一些代码,以获得一个简短的、不言自明的示例。但到目前为止,我无法在此处重现访问冲突。

#include <map>
#include <memory>
#include <string>
#include <iostream>

class MyClass{
public:
  MyClass(int a){
    this->a = a;
  }
  virtual ~MyClass(){ }
private:
int a;
};

class MyStaticClass{
public:
  static std::shared_ptr<MyClass> myMethod(const char* string){
    static std::map<std::string, std::weak_ptr<MyClass>> map;
    std::shared_ptr<MyClass> retVal = nullptr;
    std::map<std::string, std::weak_ptr<MyClass>>::iterator iter = map.find(std::string(string));

    if(iter != map.end()){
      retVal = iter->second.lock();
      if(!retVal){
        /* ptr is gone already, so remove it from map */
        iter = map.erase(iter);
      }
    }
    if(!retVal){
      /* not found in map OR erased - need to be created again */
      retVal = std::make_shared<MyClass>(atoi(string));
      std::weak_ptr<MyClass> weakRetVal = retVal;
      map.insert(std::make_pair(std::string(string), weakRetVal));
    }
    return std::move(retVal);
  }
};

int main(int argc, char* argv[]){
  {
    std::shared_ptr<MyClass> myIntPointer = MyStaticClass::myMethod("1");
  }
  {
    std::shared_ptr<MyClass> myIntPointer = MyStaticClass::myMethod("1");
  }
  {
    std::shared_ptr<MyClass> myIntPointer = MyStaticClass::myMethod("1");
  }
  return 0;
}

编译器/平台:VS 2012/Windows 8

编辑:到目前为止,我发现,当这个错误发生时,'map' 的大小总是再次为 0(至少根据调试器)。因此,即我以未初始化的地图启动程序(当然大小为0)。然后使用 myMethod() 添加条目 - 映射的大小为 4。现在 std::weak_ptr 到期,我再次调用 myMethod()。调试器现在显示 map.size() 将再次为 0(地图条目永远不会被删除,所以这应该是不可能的)。

Edit2: 当大小应该是 0x00000004 时,调试器也会显示 0xff000004 作为大小(当然大多数“条目”都无法显示)。本地静态存储会不会有 32bit/64bit 的问题?

【问题讨论】:

  • 第一段让我想起了Herb Sutter's favourite 10-liner
  • “但到目前为止,我无法在此处重现访问冲突。”您的原始代码是否使用来自多个线程的myMethod(以无序列方式)?
  • @DyP 不,它只是从主线程调用
  • 你不应该移动返回值std::move(retVal);。它禁止 NRVO。
  • @DyP 哦,我明白了 - 感谢您指出这一点!但是,即使我使用 Herb Sutter 的代码(使用互斥锁,以确保来自不同线程的访问没有问题),访问冲突仍然会发生。我之前使用了一个普通的全局函数和一个全局静态 std::map - 然后我从未发生过违规行为。如果您对如何追踪问题有任何建议,请告诉我

标签: c++ c++11


【解决方案1】:

我找到了解决我的问题的方法,就像 cmets 在另一段代码的堆栈/堆损坏中所建议的那样。以防万一有类似问题的人有一天会发现这个问题,我将简要描述到底出了什么问题以及我找出原因的步骤。

1.) 因为我在使用普通全局函数和全局静态 std::map 时没有遇到这个问题,这表明在类似的存储范围(局部函数静态)中存在问题。使用全局静态 std::map 时我没有发生访问冲突的原因很简单,就是我的堆栈/堆损坏发生在不同类中的不同本地函数静态对象上。因此,对同一存储范围内的对象的副作用比对不同存储范围的副作用(即本地函数静态 VS 全局静态)更有可能。

2.) 我刚刚多次执行我的代码,以在发生访问冲突时识别出一种模式,以及我的步骤是如何重现该行为。然后我注意到,如果我按下键盘上的某个键,我的访问违规就会越来越频繁地发生。这对我来说似乎已经是一个非常奇怪的副作用,让我特别检查我的输入类。

3.) 在那里我发现了一段致命的代码: 我对我的一个输入类使用了单例方法(这意味着:该类有一个静态 getInstance() 方法,该方法返回该类本身的本地函数静态对象)。提醒一下:我的访问冲突也发生在本地函数静态对象上。非常可疑......通过这个类进行更深入的研究,我找到了一个将实际键值存储在数组中的成员。数组大小是使用一些晦涩的宏声明的,这些宏简化为:

unsigned char keyValues[sizeof(unsigned short)];

当然,sizeof() 运算符不会返回类型的最大值 - 而是以字节为单位的大小(即 x86 和 x64 上的 2 - 或者在某些情况下因为对齐而返回 4)。因此,如果我将 sizeof(unsigned short) 替换为 65536(或 USHRT_MAX 来自 &lt;climits&gt;),我的代码将在没有任何访问冲突的情况下运行 clenaly... :( 愚蠢的错误会带来致命的后果...

【讨论】:

  • std::array 如果你使用 operator [] 可能会救你
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
相关资源
最近更新 更多