1

Simplified version of my c++ Class:

class Class
{
public:
    Class(uint32_t size_, uint8_t val_) buf(NULL), size(size_)
    {
         buf = new uint8_t[size];
         memset(buf, val_, size);
    }
    ~Class()
    {
        if(buf != NULL)
        {
            delete[] buf;
            buf = NULL;
            size = 0;
        }
    }
    void FakeDtor()
    {
        if(buf != NULL)
        {
            delete[] buf;
            buf = NULL;
            size = 0;
        }
    }

    protected:
        uint8_t* buf;
        uint32_t size;
}

Code of My unit test:

TEST_F(Classtest, testDestructor) 
{
    Class *buff = new Class(10,10);
    ASSERT_NE(buff->getData(), (uint8_t*)NULL);

    buff->~Class(); // buff->FakeDtor();

    ASSERT_EQ(buff->getData(), (uint8_t*)NULL);
}

When I compile code using msbuild and run UT - explicit call to dtor works and UT passes. When I use g++ to compile and run UT using gtest - explicit call to dtor seems to fail because following assertion fails. When I Use FakeDtor() instead of ~Class() UT passes both on Windows and Linuix. What can cause dtor to not execute when calling it explicity under Linux?

elklepo
  • 509
  • 4
  • 17
  • You cannot call a destructor explicit. If you wan't to destroy the object use `delete` – Jonas Juffinger Aug 12 '16 at 09:23
  • @JonasJuffinger Yes you can. But you usually shouldn't. – Baum mit Augen Aug 12 '16 at 09:24
  • @JonasJuffinger: manual calling a destructor is allowed. It is a normal operation, especially if you use the new-at operator for object creation. – Klaus Aug 12 '16 at 09:26
  • 1
    'ASSERT_EQ(buff->getData(), (uint8_t*)NULL);' access the object which you have explicitly deleted. This is undefined behavior. It simply makes no sense to set variables in an object while deleting them. – Klaus Aug 12 '16 at 09:28
  • But calling dtor explicity should not delete object, yes? – elklepo Aug 12 '16 at 09:29
  • @Klepak - *delete object* -- What does it mean to "delete an object"? Wipe it away from memory? If you believe this is what is supposed to happen, your belief is wrong. All it means is that the object cannot be used in a predictable, safe, defined manner. – PaulMcKenzie Aug 12 '16 at 09:40
  • BTW: because manually destruvtion will not overwrite the memory, the operation should work, also if it is UB. Is '`getData` a virtual method? If so, you access the vtable pointer which is definitely not longer usable. If you get the pointer to the data before you execute the destructor, your read should work but is still senseless. – Klaus Aug 12 '16 at 09:45
  • BTW: because manually destruvtion will not overwrite the memory, the operation should work, also if it is UB. Is '`getData` a virtual method? If so, you access the vtable pointer which is definitely not longer usable. If you get the pointer to the data before you execute the destructor, your read should work but is still senseless. – Klaus Aug 12 '16 at 09:47
  • 1
    @Klaus *"the operation should work, also if it is UB."* This statement does not make sense. Undefined Behavior is undefined, there is not "should work" after that. – Baum mit Augen Aug 12 '16 at 09:49
  • I've added also method GetSize(). Before calling dtor "buf" value was e.g. 0x12345678 and "size" value was 10. After calling dtor GetData() returned 0x12345678 and GetSize() returned 10. It seems that dtor is not called at all. – elklepo Aug 12 '16 at 09:52
  • 1
    @Klepak You have not internalised what “undefined behaviour means”: **You cannot inspect the object after calling the destructor** reliably. The values you get are bogus, they do not represent what actually happened. The fact that your compiler outputs the same values as before the destructor call is meaningless. It can display any values it likes here. – Konrad Rudolph Aug 12 '16 at 09:53
  • Thank You It is clear now for me :) – elklepo Aug 12 '16 at 10:08

1 Answers1

2

Reading the contents of the class after its non-trivial destructor ran invokes Undefined Behavior. It does not matter that the memory the object lived in is still there because you did not delete it, the object itself is dead and can no longer be used.

Literally everything is allowed to happen if you do it anyways. The concept at hand is similar to a dangling pointer/reference, for example see this.

This being UB includes "that if a destructor sets data member values, since no valid program will ever be able to read those values, a compiler can optimise away the setting of those members." as pointed out by @hvd in a comment.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • 1
    Which, since it may not be obvious to the OP, means that if a destructor sets data member values, since no valid program will ever be able to read those values, a compiler can optimise away the setting of those members. –  Aug 12 '16 at 09:27
  • @hvd: what is the sense of manipulating data inside an object which is actually in destruction? – Klaus Aug 12 '16 at 09:30
  • @Klaus All observable side-effects of those manipulations must still happen. Assigning to built-in types has no side effects though, so that's always redundant. – Baum mit Augen Aug 12 '16 at 09:33
  • But wait, I've read somewhere on StackOverflow that calling dtor explicity is the same as calling any other class method. The dtor code is executed and nothing else, class object is not destroyed. So why class field "buf" changed its value? – elklepo Aug 12 '16 at 09:36
  • @Klepak That’s simply wrong. Calling a destructor destroys the object, by definition. It’s certainly *not* like “calling any other class method”. – Konrad Rudolph Aug 12 '16 at 09:37
  • @Klepak *"The dtor code is executed and nothing else, class object is not destroyed."* That's not correct. As soon as a non-trivial destructor begins execution, the object will definitely die. As soon as it is complete, the object is completely and utterly dead, means destructed. – Baum mit Augen Aug 12 '16 at 09:38