通过返回static_cast <base />指针导致的C ++ double free

时间:2019-02-07 03:36:01

标签: c++

我有一个工厂类,看起来像

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;
}

2 个答案:

答案 0 :(得分:0)

在您的代码中,该行

return static_cast<Base>(p.get())

不会编译。

  1. 您强制转换为值类型,而不是指针类型。
  2. 缺少结尾分号。

发布问题时,请确保编写可编译的代码。您应提供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(但是第一个指针将指向损坏的资源)