3

I was playing around with some of c++ futures, and got into something that does intrigue me.

class Base
{
       public:
          Base(){ cout<<"C: Base"<<endl;}
          ~Base(){ cout<<"D : Base"<<endl;}
};
class Derived: public Base
{
       public:
           Derived(){ cout<<"C: Derived"<<endl;}        
           ~Derived(){ cout<<"D : Derived"<<endl;}
};

class Derived2: public Derived
{
       public:
           Derived2(){ cout<<"C: Derived2"<<endl;}
           ~Derived2(){ cout<<"D : Derived2"<<endl;}
};

class Derived3: public Derived2
{
       public:
           Derived3(){ cout<<"C: Derived3"<<endl;}
           ~Derived3(){ cout<<"D : Derived3"<<endl;}
};

void main()
{
        Derived *Var = new Derived2();
        delete (Derived3*)Var;  //<---- this should cause some type of run-time error
}

Why does the above not generate an error. Is it because there is not data in Derived3 to release. Or am I missing something?
But instead it outputs

C: Base
C: Derived
C: Derived2
D : Derived3       <--- SHOULD NOT BE POSSIBLE
D : Derived2
D : Derived
D : Base

4 Answers 4

6

C++ language doesn't have any extensive system of "run-time errors". Some language features can throw exceptions or call terminate(), which are "run-time errors" indeed, but invalid delete is not one of these features.

Doing something as invalid as what you are dong is causing undefined behavior in C++. Undefined behavior means that anything can happen, anything is possible. Your program might even behave as if it is "working" in some way. This is what you observe.

Experimenting with undefined behavior is a pointless exercise. The results you observe mean absolutely nothing and are generally not repeatable.

Sign up to request clarification or add additional context in comments.

1 Comment

Maybe it's not called a run-time error, but terminate is there to put you in your place if you get really naughty...
2

Your cast to Derived3 is a C-style cast which is effectively the same as the following C++-style cast.

delete reinterpret_cast<Derived3*>(Var);

Both of these casts tell the C++ runtime that you want to force the runtime to interpret the memory referenced by the cast (Var) as the given type (Derived3*) and fully understand the repercussions. Consequently, no error checking is performed.

If you are concerned about the validity of the cast, use the following:

static_cast<Derived3*>(Var) : Generates a compilation error if the cast is invalid. dynamic_cast<Derived3*>(Var) : Returns 0 (null) if the cast is invalid.

1 Comment

A C-style cast from base pointer to derived pointer is actually treated as a static_cast, not a reinterpret_cast. In this example, the static_cast is well formed and does not generate a compilation error, though most uses of the resulting pointer are Undefined Behavior.
0

It isn't causing an error because your destructor doesn't change or touch any local state.

In a typical destructor, you would free some resource allocated by that instance, which would cause the problems you'd expect.

That being said, it's still not good code, and only occurs because you're explicitly casting to the incorrect type. The behavior is technically undefined in this case, which means anything can happen, including "working" as it's doing now.

Comments

0

Undefined Behavior can do anything, including appearing to work. It's not the job of the compiler or runtime to complain if you did something illegal, just to do the right things if you didn't do anything illegal.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.