Почему это не утечка памяти в C ++? - PullRequest
0 голосов
/ 19 ноября 2018

Пару месяцев назад я задал этот вопрос, где я спросил, почему произошла утечка памяти.Видимо, я забыл виртуальный деструктор.

Теперь я пытаюсь понять, почему это не утечка памяти:

#include <iostream>
#include <vector>
#include <memory>


using namespace std;

class Base{
public:
    explicit Base(double a){
        a_ = a;
    }
    virtual void fun(){
        cout << "Base " << a_ << endl;
    }

protected:
    double a_;
};


class Derived : public Base{
public:
    Derived(double a, double b): Base(a), b_{b}{
    }
    void fun() override{
        cout << "Derived " << a_ << endl;
    }
private:
    double b_;
};



int main() {

    vector<unique_ptr<Base> > m;

    for(int i=0; i<10; ++i){
        if(i%2 == 0){
            m.emplace_back(make_unique<Base>(i));
        }else{
            m.emplace_back(make_unique<Derived>(i, 2*i));
        }
    }

    for(const auto &any:m){
        any->fun();
    }

    return 0;
}

Обратите внимание, что у меня нет виртуального деструктора для Base.

Я думал, что из-за того, что у меня есть вектор m типа unique_ptr<Base>, вызывается только деструктор из Base, и моя переменная b_ в Derived протекает, но, по словам Вэлгринда, это не такдело.Почему это не утечка памяти?

Я проверил это с помощью valgrind-3.13.0

Ответы [ 3 ]

0 голосов
/ 19 ноября 2018

Удаление объекта с помощью полиморфного указателя, когда в базовом классе отсутствует виртуальный деструктор, является неопределенным поведением.

Неопределенное поведение может означать, что ваш код теряет память, вылетает или работает отлично.

В этом случае библиотека времени выполнения предположительно выделила один блок памяти для вашего объекта и способна правильно удалить этот блок, даже когда на него указывает указатель другого типа. Это, вероятно, верно для большинства сред выполнения, но нет никаких гарантий. Например. при использовании malloc() и free() вам не нужно указывать размер от malloc() до free(), то же самое происходит и здесь.

Если бы вы определили деструктор в Derived, вы бы увидели, что он не вызывается.

0 голосов
/ 19 ноября 2018

Это не приводит к утечке памяти из-за поведения вашей реализации C ++, но это неопределенное поведение, и вы должны это исправить.

В данном случае это не утечка памяти, потому что...

  1. std::make_unique выделяется с использованием new:

    template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
    Возвраты: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
    [ unique.ptr.create]

  2. std::unique_ptr освобождает, используя std::default_delete, который использует operator delete.

Это не имеет значения с точки зрения памяти сообщает, что типы различаются, потому что delete будет по-прежнему вызываться с указателем на объект, выделенный new.

Это также будет утечка памяти, если Derived не былотривиальный деструктор.Например, если он содержит std::vector, то деструктор vector будет вызван ~Derived.Если он не вызывается, хранилище для vector будет (обнаружимо) утечкой.

См. Также: Как работает удаление?

Однако все этонеопределенное поведение в соответствии со стандартом C ++, поэтому теоретически оно может перестать работать в любое время.См. [expr.delete / 3] .

В выражении удаления из одного объекта, если статический тип удаляемого объекта отличается от его динамического типа ивыбранная функция освобождения (см. ниже) не является уничтожающим оператором удаления, статический тип должен быть базовым классом динамического типа удаляемого объекта, а статический тип должен иметь виртуальный деструктор или поведение не определено.

0 голосов
/ 19 ноября 2018

Произошла бы утечка памяти, если бы b был объектом, у которого были ресурсы (память, сеть ...) из-за неопределенного поведения.

Здесь, случайно, производный деструктор не делаетвсе, и память для объекта освобождается должным образом (на этот раз).Но что-то большее, чем встроенные / тривиально разрушаемые типы, может вызвать утечку памяти (я предлагаю вам попробовать vector размером 10, например).

Кстати, o не используется?

...