c++ polymorphisam deleting a object, constructor concearn

Started by
3 comments, last by Serapth 10 years, 7 months ago

Hello.

I was just doing a sketch of polymorphism for my project and i came to a following situation that made me think.

If i have base and derived classes, i declare a pointer to base class and set it to (new derived class) and then call delete base class object i made.

The deconstructor for base class is called? and the object is holding derived class!

Thus making memory leak? or em i missing something;

class Base
{
public:
    Base(){}
    int q;
};

class Derived : public Base
{
public:
    Derived(){}
    int q,w;
};

int main()
{
    Base * one;
    one = new Derived;
    delete one;
    return 0;
}

Here a leak of one int size will occur?

If so, how would the base class know to call its pointing to deconstructor?

Advertisement

You need a virtual function (the destructor in this case) for runtime dynamic dispatch to happen. Without virtual, you get static dispatch, which is based on the static type of the object you use. In this case, the static type of the pointer is Base *, so that's the type determining which destructor is called.

Here a leak of one int size will occur?

No.

If so, how would the base class know to call its pointing to deconstructor?

Make the base class destructor virtual.


In C++, it is considered good practice to use a virtual destructor:
class Base {
public:
    virtual ~Base() {
    }

private:
    bool mTest;
};

class Derived : public Base {
public:
    Derived() {
        mData = new int[1000];
    }

    ~Derived() {
        delete[] mData;
    }

private:
    int* mData;
    double mOtherData;
};

int main() {
    Base* p = new Derived;
    delete p;
}
If Base::~Base were not virtual, then Derived::~Derived would not be called, and the memory for its mData would not be released. The members for Derived (the pointer mData, mOtherData, and mTest) would not leak in either case, because the members reside inside the memory block allocated for Derived. Your C++ runtime and operating system keep track of allocated blocks, so the runtime/operating system is smart enough to know the size of a block given only the pointer to its address. You can technically do something like this:
void magic( void* ptr ) {
    // Neither ~Base() or ~Derived() will be called because we won't know what type ptr is!
    // But the memory block will be freed regardless.
    delete ptr;
}

int main() {
    magic( (void*)(new Derived) );
}
Whether it is morally acceptable to do or not is another question...


void magic( void* ptr ) {
    // Neither ~Base() or ~Derived() will be called because we won't know what type ptr is!
    // But the memory block will be freed regardless.
    delete ptr;
}

int main() {
    magic( (void*)(new Derived) );
}
Whether it is morally acceptable to do or not is another question...

The question now is, the memory block will be freed at end of runtime of the application, or at end of scope?

It seems i do not understand the clogs beneath as i thought i did.

Thank you on your help, i appreciate it.



void magic( void* ptr ) {
    // Neither ~Base() or ~Derived() will be called because we won't know what type ptr is!
    // But the memory block will be freed regardless.
    delete ptr;
}

int main() {
    magic( (void*)(new Derived) );
}
Whether it is morally acceptable to do or not is another question...

The question now is, the memory block will be freed at end of runtime of the application, or at end of scope?

It seems i do not understand the clogs beneath as i thought i did.

Thank you on your help, i appreciate it.

The memory is freed as soon as delete is called. Or more accurately, the destructors are called the minute delete is called. On the other hand, going out of scope will also result in a destructor being called if you are dealing with a non-pointer type, which is the principal a smart pointer works under ( it's a heap based object holding a stack based pointer, so when it goes out of scope, it's destructor decides what to do with the pointer it holds ). You should really look into the idiom Resource Acquisition is Initialization, which is more or less the established norm for dealing with dynamic memory in C++. It will take away a great many of the memory leak gotchas that C++ is prone for. The virtual destructor thing though... that's just a wart on the language.

As to what order constructors/destructors are called, objects are constructed from the base class up. Being the base class constructor gets called, then any derived classes get constructed. In the event of a virtual destructor, it is exactly the opposite, so the destructor of the derived class gets called first, then the base classes destructor gets called. If you forget to make a class destructor virtual, it will not be called at all, resulting in a leak. In the case of multiple inheritance, it's a bit more tricky, I believe its done by the order classes are inherited ( as in class A : B, C {} ) will call B's constructor first, then C's, then when destructors are called, it will call first C, then B, then A.

This topic is closed to new replies.

Advertisement