C++ двойной бесплатно вызваны возвращающая static_cast <Base> указатель

Вопрос задан: 8 месяцев назад Последняя активность: 8 месяцев назад
up -1 down

У меня есть класс фабрики, который выглядит как

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 является членом завода. Я попал в аварию на «двойной бесплатно», когда возвращается указатель Изменение создать функцию

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 создает указатель, который разделяет счетчик ссылок с р, и поэтому позже удаление возвращенного указателя базы не приведет к высвобождению ресурсов до тех пор, как р-прежнему содержатся в векторе. Тем не менее, я до сих пор не понимаю, почему код разбился, когда указатель находится в m_p была перезаписана. На данный момент, количество ссылок должны идти на 0, и это освободит ресурс (как счетчик ссылок не используется совместно с указателем в векторе завода). Так что я бы ожидать, что это вызовет проблемы, когда код будет в какой-то момент ссылки на указатель р провел на заводе-изготовителе. Тем не менее, появляется ресурс получает освобожденный во второй раз уже. Таким образом, первый раз должно быть, когда стек функции создания раскручивается. Почему это?

Пример;

#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;
}
c++

2 ответа

up 0 down

В коде, линия

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.

up 0 down

Это мое понимание того, что произошло. Крушение появляется в результате выделения памяти make_shared, который на мой компилятор делает один вызов таНос и выделяет пространство для блока управления и ресурса в виде одного блока, с блоком управления, а затем структуру. Принимая сырой указатель и создание нового общего указателя создает общий указатель, который указывает, что ресурсы и к другому блоку управления. Когда счетчик ссылок этот указатель достигает 0, он просит высвободить ресурс, а также свой собственный блок управления, однако, указатель на ресурс не указывает на начало выделения памяти (который начинается с блока управления из первого общего указатель) и так таНос броски. Замена сделать совместно с регулярным созданием общего указателя сделать бы два вызов таНос и выделить отдельный блок для блока управления и структур, поэтому позже удаление не будет бросать двойные бесплатно (но первый указатель будет затем указать коррумпированный ресурс)