Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this not a memory leak in C++?

A couple of months ago I asked this question where I asked why there was a memory leak. Apparently, I forgot a virtual destructor.

Now I'm struggling to understand why this is not a memory leak:

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

Note that I do not have a virtual destructor for Base.

I thought that because I have a vector m of type unique_ptr<Base> only the destructor from Base gets called and my variable b_ in Derived would leak but according to valgrind this is not the case. Why is this not a memory leak?

I have tested this with valgrind-3.13.0

like image 769
user7431005 Avatar asked Feb 03 '26 23:02

user7431005


1 Answers

Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.

Undefined behaviour may mean your code leaks memory, crashes or works perfectly.

In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.

If you had defined a destructor in Derived you would see that it is not being called.

like image 110
Alan Birtles Avatar answered Feb 06 '26 11:02

Alan Birtles