0

When a class is instantiated, a virtual table pointer is created to point to its virtual table. And each virtual function is assigned a function address.

Will the pure virtual function be assigned a default function address?

class base {
public:
   virtual void func() = 0;
};

class derived : public base {
public:
   virtual void func() {
      cout << "derived func" << endl;
   }
};

int main()
{
   base *ptr = new derived();
   delete ptr;
   ptr->func();
   return 0;
}

I have run the demo on VS2017 and the error "performed a bad memory access" happened. I know that the "__cxa_pure_virtual" will be called under the Linux system. So how do I implement my "__cxa_pure_virtual"? And how can I assign the function address to my pure function?

Zhen Yang
  • 83
  • 9
  • 2
    Suggestion: Don't `delete` the object before you are done using it. – user4581301 Jun 30 '21 at 02:37
  • Users might delete the object in another thread unconsciously. – Zhen Yang Jun 30 '21 at 02:40
  • C++ will handle the `virtual` magic for you. `derived` will set things up, probably an entry in a vtable, behind the scenes so you don't have to worry about it, but without a valid instance to operate on there are no guarantees of what will happen. Program could crash. Could launch the US's nuclear arsenal. Might even look like it works. If some fool deletes the object and the program goes on using it, the program is broken and you cannot reason about the outcome. – user4581301 Jun 30 '21 at 02:41
  • 2
    _"Users might delete the object in another thread unconsciously"_ That's not how any of this works. If you delete an object and then use it, you get UB -- end of discussion. If users can do this across threads without synchronization, then you can pile on additional UB. If you're providing an abstraction where someone can do this "unconsciously", then you've provided a bad abstraction. You should be using smart pointers to prevent this; `shared_ptr`s and the like will do this for you without ever having such a problem – Human-Compiler Jun 30 '21 at 02:45
  • 2
    It's also worth mentioning that in addition to the other problems, your lack of a `virtual` destructor for `base` also introduces UB as soon as you call `delete ptr`, since `ptr` is an instance of `base*` not `derived*`. Even if the destructor wouldn't do anything, this is formally UB and the compiler can choose what it wants to do with this -- even just optimize out the code, if it chose to, since the program is invalid. – Human-Compiler Jun 30 '21 at 02:48
  • Are you deleting the object under the misconception that this will somehow cause `base::func()` to be called? – JaMiT Jun 30 '21 at 02:52
  • Yes, the derived object has been deleted, and the base pointer works like a dangling pointer. – Zhen Yang Jun 30 '21 at 02:59

2 Answers2

3

Calling a method in a deleted object is undefined behaviour, so in principle anything can happen and it’s your fault, but we can explain why you saw this behaviour.

First when you have a pure virtual function, the compiler could store a null pointer into the vtable, and when you call the function it crashes. Your compiler is helpful, it stores a pointer to a function named __cxa_pure_virtual, which is designed to crash when you call it. So a crash in this function tells you the crash was due to calling a pure virtual function. This is both more convenient for debugging, and it is safer to have a defined crash.

Second you deleted ptr. Deleting a derived object runs the derived class destructor, then it turns the object into a base class object, then it calls the base class destructor. Ptr had a pointer for a real func() in its vtable, but the csecond of these three steps replaced it with a pointer to __cxa_pure_virtual. It seems the memory for the object was released but not overwritten so your code manages to call the pure virtual function.

And you absolutely one hundred percent cannot implement __cxa_pure_virtual. It is already implemented. It crashes when called and that is intentional and how it should be.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
2

Following a pointer to a deleted object is undefined behaviour.

Sometimes it will work, sometimes it will crash, sometimes it will format your hard drive.

In a typical implementation in machine code, deleted objects have their memory marked be recycled, This marking may or may not modify the data inside where the object was. At some poimt, another allocation will reuse that memory.

The vtable pointer is stored near the front of the object, and can be overwritten at any point. Or maybe not overwritten. Maybe it gets a pointer to a different vtable, which has a delete directory function there. Or maybe thr compiler does whole program optimization and proves every valid pointer to base is a pointer to derived, and devirtualizes every call.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524