Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly delete pointers when using abstract classes

Tags:

c++

Suppose I have the following classes and objects:

#include <iostream>

class Animal
{
public:
    virtual void makeNoise() = 0;

    void eat()
    {
        std::cout << "Eating..." << "\n";
    }

    void sleep()
    {
        std::cout << "Sleeping..." << "\n";
    }
};

class Cat: public Animal
{
public:
    void makeNoise()
    {
        std::cout << "Miow..." << "\n";
    }
};

class Cow: public Animal
{
public:
    void makeNoise()
    {
        std::cout << "Mooo..." << "\n";
    }
};

int main()
{
    Animal *animal;
    Cat *cat = new Cat();
    Cow *cow = new Cow();

    animal = cat;
    animal->eat();
    animal->sleep();
    animal->makeNoise();

    animal = cow;
    animal->eat();
    animal->sleep();
    animal->makeNoise();

    return 0;
}

Note that animal is an abstract class.

How can I properly delete the pointers animal, cat and cow?

When I try to delete animal; I get the following warning message:

warning: deleting object of abstract class type 'Animal' which has non-virtual destructor will cause undefined behaviour.

In the other hand, when I try to delete cat; I get the following message:

warning: deleting object of polymorphic class type 'Cat' which has non-virtual destructor might cause undefined behaviour.

like image 757
KelvinS Avatar asked Dec 07 '17 19:12

KelvinS


People also ask

Can abstract class have pointer?

You can't create an object of an abstract class type. However, you can use pointers and references to abstract class types.

Can abstract class have destructor?

You can create an abstract base class with only a virtual destructor.

Do pointers have to be deleted?

No. The only exception to that would be if deltaTime was created with new and it was the responsibility of Update to return the memory (unlikely, and a poor design). like you would with any other pointer? Just because something is a pointer does not mean you should call delete .

Does deleting a pointer call the destructor?

Default destructors call destructors of member objects, but do NOT delete pointers to objects. Thus, we need to write destructors that explicitly call delete.

Why can’t I delete the pointers to 2 from a vector?

Then we end up with a memory leak: the vector no longer contains the pointers to 2, but no one has called delete on them. So we may be tempted to separate std::remove_if from the call to erase in order to delete the pointers at the end of the vector between the calls: But this doesn’t work either, because this creates dangling pointers.

What is modified type in remove_pointer?

Makes type from pointer to type. The type to modify. An instance of remove_pointer<T> holds a modified-type that is T1 when T is of the form T1*, T1* const, T1* volatile, or T1* const volatile, otherwise T.

How to delete a string literal from a class?

A string literal exists for the whole duration of the program and can't be deleted. What you might want to do in your constructor is to create a copy of the string. Otherwise you will have problems if the string that is passed to the constructor is not a string literal because it could be destroyed before the class object is destroyed.

What will happen if I delete an object with non-virtual destructor?

warning: deleting object of polymorphic class type 'Cat' which has non-virtual destructor might cause undefined behaviour. c++ Share Improve this question Follow asked Dec 7, 2017 at 19:53


1 Answers

A basic C++ rule says that destructors work their way up from the derived class to the base class. When a Cat is destroyed, then the Cat part is destroyed first and the Animal part is destroyed after.

delete animal; is undefined behaviour because in order to properly follow C++ destruction rules, one must know, at runtime, which derived class part should be destroyed before the Animal base part. A virtual destructor does exactly that - it enables a dynamic dispatch mechanism that makes sure destruction works as designed.

You have no virtual destructor, however, so delete animal just doesn't make sense. There is no way to call the correct derived-class destructor, and destroying only the Animal part wouldn't exactly be meaningful behaviour, either.

Therefore, the C++ language makes no assumptions about what will happen in such a situation.

Your compiler is nice enough to warn you about this.


With delete cat, the situation is slightly different. The static type of the cat pointer is Cat*, not Animal*, so it is clear even without any dynamic dispatch mechanism which derived-class destructor to call first.

The compiler still warns you about this, but it does so with a different wording ("might cause" vs. "will cause"). I believe the reason is that Cat might itself be the base class for more derived classes, seeing as it is already part of a class hierarchy with virtual functions.

It apparently doesn't bother to execute a more complete code analysis to find out that delete cat is really harmless.


In order to fix this, make the Animal destructor virtual. While you're at it, replace your raw pointers with std::unique_ptr. You still have to follow the virtual destructor rule for classes like yours, but you no longer have to perform a manual delete.

like image 199
Christian Hackl Avatar answered Oct 18 '22 20:10

Christian Hackl