-2

lets take an c++ example:

class A
{
public:
    A() { cout << "hey" << endl; }
    ~A() { cout << "by" << endl; }


};


class B : public A
{
public:
    B() {}
    virtual ~B() { cout << "from b" << endl; }

};
int main()
{
    A * a = new B[5];



    delete[] a;

    return 0;
}

the results of this code is an infinite loop of "by" , why is this? B vtable supposed to be upcast to A that don't have vtable , so i would expected it to throw an exception when he try to reach the virtual constructor.

p.s. where i can read on all kind of examples of wired constructor destructor behave? (with examples )

joy
  • 1
  • 1
  • [When to use virtual destructors?](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors) – Cory Kramer Jun 22 '17 at 17:15
  • 1
    @CoryKramer I do not think this is exact duplicate, OP does not understand why making virtual dtor only in derived class does not work. – Slava Jun 22 '17 at 17:24
  • Welcome to Stack Overflow. Please take the time to read [The Tour](http://stackoverflow.com/tour) and refer to the material from the [Help Center](http://stackoverflow.com/help/asking) what and how you can ask here. – πάντα ῥεῖ Jun 22 '17 at 17:25
  • this is a bit different q. , i know how to fix it , it's not about that , it's about the loop that doesn't supposed to be. – joy Jun 22 '17 at 17:38

2 Answers2

3

It is undefined behavior to delete an array of B as an array of A. This is true so long as A has a non-trivial destructor (when it does have a trivial destructor, it may be defined, I do not recall). Inheritance, virtual -- either matter to its undefinedness in this case.

Everything after that is better than your harddrive's image files and browser password cache being emailed to your contact list, which is a legal example of "undefined behavior" under the C++ standard. You got lucky.


In particular, I'd guess that A is a trivial object. and delete[] is expecting to delete a series of size-1 trivial objects. Somehow the fact that B is much larger and non-trivial (it contains a vtable for one) has messed your compiler up and led to your infinite loop.

Maybe the format it stores the information on how to delete an array is different with trivial objects with non-virtual destructors. With the vtable case, maybe it stores a function pointer there, and in the trivial case it stores a count.

The loop then isn't infinite, but rather iterates (nearly random 32 or 64 bit number) times, as pointer values tend to be relatively random.

If you really care what specific undefined behavior your code resulted in on this particular version of this particular compiler on this particular system with this particular context, you can godbot your code. But I don't see why you should care.


C++ vtables are only created for a type if needed for that type. Inheriting and adding virtual afterwards doesn't change the fact the type does, or does not, need a vtable.

Raw arrays in C are not contravariant nor covariant. If you have an array, you cannot safely convert it to a pointer to any different type (there are some extremely narrow exceptions involving standard layout and raw bytes/chars and the like).

If we go back to your example and remove the array:

A * a = new B;

delete a;

this gets less bad. The delete a remains UB, as you are deleting a B as an A.

Without virtual ~A() in A, you cannot delete a B as an A.

To fix this we add:

virtual ~A() { cout << "by" << endl; }

and now A has a vtable -- instance of A carry a pointer to a vtable, which (among other things) tells the compiler how to delete an A or a derived A type.

Now the code is well defined, and it prints

hey
from b
by

if my head-compiler got it right.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • i know how to fix it , it's an wired end-case that i don't understand , i know that it supposed to do a memory leak , but why there is a loop??? why that happened? – joy Jun 22 '17 at 17:37
  • @joy You did undefined behavior. That is paragraph 1. That is "why there is a loop". Undefined behavior means *anything can happen*. An infinite loop is a subset of "anything", so it fully explains the loop's "why". Now, I also I explained a possibility of why the compiler might output a seeming infinite loop (it is interpreting a pointer in one case as a count in another). What isn't clear? – Yakk - Adam Nevraumont Jun 22 '17 at 17:40
0

the results of this code is an infinite loop of "by" , why is this?

Because base class A does not have virtual destructor

B vtable supposed to be upcast to A that don't have vtable

It does not work that way. When this code:

delete [] a;

compiled, compiler uses definition of class A which does not have vtable at all. The fact, that derived class has vtable does not matter here.

so i would expected it to throw an exception when he try to reach the virtual constructor.

your expectation is wrong. First of all there is no such thing as virtual constructor. Second - you work with class A in this case (through A*). If derived class has vtable or not does not matter in this case.

Note: C++ language does not dictate that virtual function resolution has to be done through vtable, though it is pretty common to implement it this way. I used this term because you put it into question.

Slava
  • 43,454
  • 1
  • 47
  • 90