user2227713
user2227713

Reputation: 411

C++ Inheritance quiz

I was recently asked a basic C++ question that I didn't actually know the answer to and I never found out what the answer was. Here is the question:

When this program is run it can cause an error. What change would you make to the Base class to fix it?

#include <iostream>

class Base {
public:
    virtual void ShowMessage () {
        std::cout << "Base class message\n";
    }

    // Something should be added here!
};

class Derived : public Base {
public:
    explicit Derived (const std::string & value)
        : myValue(value)
    {
    }

    virtual void ShowMessage () {
        std::cout << "Derived class message " << myValue << "\n";
    }

private:
    std::string myValue;
};

int main () {
    Base * obj = new Derived("Test message");
    obj->ShowMessage();
    delete obj;
}

It compiles and runs just fine on my machine. Anyone know what they were looking for?

Upvotes: 3

Views: 1087

Answers (3)

Tony Delroy
Tony Delroy

Reputation: 106196

A virtual destructor. When this code executes...

delete obj;

...it won't know there's a std::string to destruct/deallocate unless the destructor is invoked polymorphically (i.e. via virtual dispatch). That's because without virtual dispatch, the Base class destructor runs, and it doesn't have that std::string data member to worry about, so doesn't include the code related to it.

The guideline's simple: whenever you are deleting objects using a pointer to a base class, you should ensure the base has a virtual destructor.

Anything else has the problems above, and is technically undefined behaviour. While it's not a good idea to attempt to "reason" about undefined behaviour because you can't be sure whatever happens won't be worse than expected (if not now, then on some furture compiler/compiler-version, or with different compilation switches or surrounding code, another CPU etc), memory leaks are an obvious likely consequence in this case: the std::string constructor can be expected to have dynamically new-ed a character array, and the instructor would have deleted it if run. Still, there can be other problems - if data members of the derived class are say shared pointers then the pointed-to objects may not be destructed/deallocated properly, if there are file handles, shared memory handles, threads, locks etc. held the consequences of failing to invoke the destructors could be severe: data may not be flushed out to intended destinations, the application may run out of resources after repeated leaks, or it could hang later when some other code tries to acquire a lock it's still holding....

Upvotes: 4

Alexander Mihailov
Alexander Mihailov

Reputation: 1152

You need to add a virtual destructor to the Base class.

class Base {
public:
    virtual void ShowMessage () {
        std::cout << "Base class message\n";
    }

    virtual ~Base() {}
};

In your case, the Derived::myvalue will not be released properly.

Upvotes: 6

awesoon
awesoon

Reputation: 33691

You must make destructor virtual, since deleting polymorphic type without virtual destructor leads to undefined behavior:

Per 5.3.5.3:

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

Upvotes: 1

Related Questions