-2

Here is the program producing the error:

#include <iostream>
#include <string>
using namespace std;

class Pet {
protected:
    string name;
public:
    Pet(string nam):name(nam){}
    ~Pet(){cout<< "Pet destroyed" << endl;} // attention
    void play();
};

void Pet::play(){
    std::cout<< "Pet " << name <<" is playing ..." <<endl;
}

class Cat : public Pet {
public:
    Cat(string nam):Pet(nam){}
    ~Cat(){cout<< "Cat destroyed" <<endl;}
    void play();
};

void Cat::play(){
    cout<< "cat " << name <<" is playing ..." <<endl;
}

class Dog : public Pet {
public:
    Dog(string nam):Pet(nam){}
    ~Dog(){cout<< "Dog destroyed" <<endl;}
    virtual void play(); // attention
};

void Dog::play(){
    cout<< "dog " << name <<" is playing ..." <<endl;
}

int main(void) {
    Pet *cat1 = new Cat("Kitty");
    Pet *dog1 = new Dog("Tom");
    Pet *pet = new Pet("Nicky");

    cat1->Pet::play();
    dog1->play();
    pet->play();

    delete cat1;
    delete pet;
    delete dog1;
    return 0;
}

This program just compiles fine, but when I run it I got the following output:

Pet Kitty is playing ...
Pet Tom is playing ...
Pet Nicky is playing ...
Pet destroyed
Pet destroyed
Pet destroyed
t(35634,0x7fff7ae04310) malloc: *** error for object 0x7f8623c03b68: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

The results before the error information just make sense, but I couldn't understand why I got this error. The error produces at the line delete dog1;. 0x7f8623c03b68 is the address of the object dog1. Since dog1->play(); just executes fine, the memory space for object dog1 should be well allocated. Why does the error says "pointer being freed was not allocated"???

When I change the destructor of class Pet to be virtual, the program executes fine without errors. Why??? Here is the output:

Pet Kitty is playing ...
Pet Tom is playing ...
Pet Nicky is playing ...
Cat destroyed
Pet destroyed
Pet destroyed
Dog destroyed
Pet destroyed

In this case, when delete dog1, the destructor of Dog will first be called then the destructor of Pet. So what differences concerning the changes in memory does the word "virtual" make in these two cases?

In this article Why should I declare a virtual destructor for an abstract class in C++? it explains that when the destructor of a base class is not declared virtual, when delete a pointer of base class type that referring to a subclass object, un Undefined behavior will be produced. So is this error just an example of the so called Undefined behavior? Since when I removed the virtual from the class Dog, the program executes well.

I am note sure if it's a duplicated question as the problem mentioned in the above article. Just want to make sure and find the reason for that error.

Any instructions will be welcomed. Thanks.

Community
  • 1
  • 1
qingl97
  • 327
  • 4
  • 14

2 Answers2

2

The line delete cat1; causes undefined behaviour because cat1 has type Pet *, however Pet does not have a virtual destructor, and the pointer actually points to an instance of derived class.

When the behaviour is undefined, anything can happen. To fix this you need virtual ~Pet(), as you noted.

I'd guess that the malloc error will be relating to the class layout for Dog: Dog needs a vtable so perhaps a larger area was allocated that didn't start at the same address as dog. (Someone familiar with vtable layouts could probably confirm this)

M.M
  • 138,810
  • 21
  • 208
  • 365
0

When I change the destructor of class Pet to be virtual, the program executes fine without errors. Why???

Because the standard guarantees correct behavior only when the destructor is virtual:

5.3.5 Delete

  1. In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45