Does not C ++ work well?

7

I have not played c ++ for a long time and I have forgotten so much that maybe the doubt I have or the possible failure is really something basic in the language.

I have written a small code to test the overhead of the delete operator in c ++ with a virtual destructor and I have found that it only works correctly the first time you call delete.

The code is here (it's a page that allows you to write code online and execute it, I've tried it in visual studio and gcc and it's exactly the same thing): link

Could someone tell me why this is happening? Only the first time works well and the second one is not working well

I put the code here also and the result it gives:

#include <stdio.h>
class Base
{
public:
    virtual ~Base() { printf("~Base\r\n"); }
    void operator delete(void *m) { printf("delete Base\r\n"); }
};
class Derived : public Base
{
public:
    Derived() { x = 1; }
    ~Derived() { printf("~Derived\r\n"); }
    void operator delete(void *m) { printf("delete Derived\r\n"); }
    int x;
};

int main()
{
    Derived *derived = new Derived();
    printf("1) delete derived (%i)\r\n", ((Derived*)derived)->x);
    delete derived;
    printf("2) delete derived (%i)\r\n", ((Derived*)derived)->x);
    delete derived;
    return 0;
}

Result:

1) delete derived (1)
~Derived
~Base
delete Derived
2) delete derived (1)
~Base
delete Base

Now, if we remove the "virtual" class called "Base" everything is perfect, logically whenever we call the delete with "Derived" (the code is ready to remove the virtual and nothing fails). To run it: link

Thanks

    
asked by Lord 29.08.2018 в 21:02
source

2 answers

7

The question " Does C ++ not work well? " makes as much sense as asking " Does the Spanish language not work well? ".

The language works, it can have its points to improve (that's why the standard is in continuous review) even its failures (that's why defects are collected, which are studied by the standards committee) but work: it works ... yes no: you could not even write compilers, right?

Your code is invoking undefined behavior because you are using objects whose life cycle has ended, if we consult the C ++ standard in section 3.8 (my translation):

  

3.8 Object life time

     

[...]

     

The lifetime of an object T ends when:

     
  • (1.3) If T is an object with a non-trivial destructor (12.4), the destructor starts, or
  •   
  • (1.4) the storage that the object occupies is reused or released.
  •   

You can see in your code that the destructor has been called, so the lifetime of your object has ended if it has no trivial destructor (translation and highlighted mine):

  

12.8 Destroyers

     

[...]

     

A destructor is trivial if it is not provided by the user and if:

     
  • (5.4) the destructor is not virtual ,
  •   
  • (5.5) all direct base classes in your class have trivial destructors and
  •   
  • (5.6) for all non-static member objects of the class that are not of the class type (ergo formation), each of them has a trivial destructor.
  •   

Your object has a defined destructor and its base class has destructor defined so neither your derived class nor your base class are trivial. And it's indefinite behavior because (translation and highlighting mine):

  

3.8 Object life time

     

[...]

     
  • [...] The program has undefined behavior if:

         
    • (5.1) the object will be or was a class with non-trivial destructor and the pointer is used as the operand of a expression-delete
    •   
  •   

    You use the pointer to an object (with non-trivial destructor) whose life cycle has ended in a delete expression.

    Conclusion

    C ++ does not work badly, it has rules that when not followed " is not responsible for what happens ".

        
    answered by 30.08.2018 в 09:12
    4

    As you have indicated in another answer, it is an undefined behavior .

    No , you do not release the memory 2 times; but the delete job is not just freeing up memory; is also calling the destructor . And that if you do it 2 times.

    From experience, I know that compilers do curious things when calling a destructor of a class with virtual member functions: to help the programmer discover errors, they usually modify the pointer to the function table virtual (VTABLE), so that later calls to functions of that instance cause a serious error.

    That's what's happening: the first call to delete modifies the pointer to the VTABLE; the next call, to put it somehow, you are not really calling the Base destructor, but something else, which the compiler has decided for you **.

    That explains why everything works fine by removing the destructor virtual . If the class does not have virtual functions (or virtual inheritance), a pointer to VTABLE is not used; the calls to the destructors are normal (they just execute the user code), and the messages that you expect are displayed.

        
    answered by 30.08.2018 в 06:19