我有一个工厂类,看起来像
class Base;
class Derived;
class Factory {
std::vector<std::shared_ptr<Derived>> m_vector;
std::shared_ptr<Base> create() {
std::shared_ptr<Derived> p = std::make_shared<Derived>();
m_vector.push_back(p);
return static_cast<std::shared_ptr<Base>>(p.get());
}
};
class Foo {
void doStuff() {
std::shared_ptr<Base> m_p = m_factory->create();
// ....
m_p = m_factory->create(); // here the code crashed for double free
};
其中m_vector是Factory的成员。当返回的指针将create函数更改为
时,我发生了“双重释放”崩溃std::shared_ptr<Base> Factory::create() {
std::shared_ptr<Derived> p = std::make_shared<Derived>();
m_vector.push_back(p);
return std::dynamic_pointer_cast<Base>(p);
}
解决了该问题。我知道dynamic_pointer_cast创建了一个与p共享引用计数的指针,因此只要p仍包含在向量中,以后删除返回的基本指针将不会导致资源的释放。 但是,我仍然不明白为什么存储在m_p中的指针被覆盖时代码会崩溃。此时,引用计数应为0,这将释放资源(因为引用计数未与工厂向量中的指针共享)。因此,当代码在某个时候引用工厂持有的指针p时,我希望这会引起问题。但是,看来资源已经第二次被释放了。因此,第一次必须是create函数的堆栈展开时。 为什么会这样?
示例;
#include "memory"
#include "iostream"
struct Foo {
};
struct FooTwo : public Foo {
};
struct Bar {
std::shared_ptr<FooTwo> m = std::make_shared<FooTwo>();
std::shared_ptr<Foo> getFoo()
{
std::shared_ptr<FooTwo> p = std::make_shared<FooTwo>();
// std::shared_ptr<FooTwo> p = std::shared_ptr<FooTwo>(new FooTwo());
m = p;
return std::shared_ptr<Foo>(p.get());
}
};
int main()
{
Bar b;
std::shared_ptr<Foo> d = b.getFoo();
std::cout << "Here 1" << std::endl;
d = nullptr;
std::cout << "Here 2" << std::endl;
return 0;
}
答案 0 :(得分:0)
在您的代码中,该行
return static_cast<Base>(p.get())
不会编译。
发布问题时,请确保编写可编译的代码。您应提供MCVE(https://stackoverflow.com/help/mcve)。
您的代码或多或少等于:
std::shared_ptr<Derived> p = std::make_shared<Derived>();
auto *raw_pointer = p.get();
auto *base = static_cast<Base *>(raw_pointer);
std::shared_ptr<Base> ret_value(base);
因此,您从单个指针创建了2个不同的std::shared_ptr
。每一个都有一个计数。
猜猜是什么!当第一个shared_ptr
超出范围(函数退出)时,该对象将被删除。然后将另一个指针移到m_p
中。
到那时,原始对象已经被销毁,因此
doStuff
使用已经删除的对象,这是未定义的行为。
doStuff
完成后,m_p
也会超出范围,并尝试第二次删除对象。
您不应从不通过在另一个shared_ptr
上调用get
来创建shared_ptr
。
仅当确实需要原始指针时,才应调用get
方法。并且在执行此操作时,应确保:
shared_ptr
超出范围后,您将不再使用原始指针,以确保对象一旦删除就不会使用。shared_ptr
。更新
如果要查看计数,可以使用函数use_count
进行调试。参见https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count。
答案 1 :(得分:0)
这是我对发生的事情的理解。崩溃似乎是由于make_shared的内存分配导致的,该内存分配器在我的编译器上对malloc进行了一次调用,并在单个块中分配了控制块和资源的空间,首先分配了控制块,然后分配了结构。获取原始指针并创建一个新的共享指针,将创建一个指向该资源和另一个控制块的共享指针。当该指针的引用计数达到0时,它请求释放资源以及它自己的控制块,但是,指向该资源的指针并不指向内存分配的开始(它从第一个共享的控制块开始指针),因此malloc引发。 用常规创建共享指针替换make shared将对malloc进行两次调用,并为控制块和结构分配一个单独的块,因此以后的删除不会抛出double free(但是第一个指针将指向损坏的资源)