【发布时间】:2013-09-28 17:51:36
【问题描述】:
我的问题有点复杂,但我尽量简单地描述它。
我想在我的一个类中使用本地静态成员(std::map<std::string, std::weak_ptr> 类型)实现一个静态方法。每次调用此方法时,它都应该查找映射中是否存在以传递的参数为键的对象。该方法返回的值是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 - 然后我从未发生过违规行为。如果您对如何追踪问题有任何建议,请告诉我