Jul 14, 2014

[C++] private delete operator member function issue

Reference:
http://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/
C++ deleting destructors
C++20 destroying deallocation functions; tl;dr version on stack overflow 

  
#include <new>

class Base {
public:
  virtual ~Base() {}
};

class Derived : public Base {
public:
  ~Derived() {}
private:
// Since member allocation and deallocation functions are static they cannot be virtual.
  void* operator new(size_t);
// Since member allocation and deallocation functions are static they cannot be virtual.
  void* operator new[](size_t);
// Any deallocation function for a class X is a static member (even if not explicitly declared static).
  void operator delete(void*);
// Any deallocation function for a class X is a static member (even if not explicitly declared static).
  void operator delete[](void*);
};

int main() {
  Derived d;
  return 0;
}
  
$ clang++ test.cpp
Undefined symbols for architecture x86_64:
  "Derived::operator delete(void*)", referenced from:
      Derived::~Derived() in test-LLdS56.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Quoted:
C++ mandates that you must pass the exact same address to operator delete as what operator new returns.

When you’re allocating an object using new, the compiler implicitly knows the concrete type of the object (which is what the compiler uses to pass in the correct memory size to operator new, for example.)

However, if your class has a base class with a virtual destructor, and your object is deleted through a pointer to the base class, the compiler doesn’t know the concrete type at the call site, and therefore cannot compute the correct address to pass to operator delete().

Why, you may ask?  Because in presence of multiple inheritance, the base class pointer’s address may be different to the object’s address in memory.

So, what happens in that case is that when you delete an object which has a virtual destructor, the compiler calls what is called a deleting destructor (which has a D0Ev suffix in the GNU toolchain, as opposed to D1Ev which is the suffix for regular destructors) instead of the usual sequence of a call to the normal destructor followed by operator delete() to reclaim the memory.

Since the deleting destructor is a virtual function, at runtime the implementation of the concrete type will be called(因destructor 吃derived object的this ptr), and that implementation is capable of computing the correct address for the object in memory.

What that implementation does is call the regular destructor, compute the correct address of the object, and then call operator delete() on that address.  The correct address is computed by looking at an offset value stored in the vtable for the object.
----------
重點:
這樣的目的其實是要呼叫 Derived Class Scope自己的 static delete operator!
此delete operator並不會有offset資訊.
offset 資訊在 vtable內, 即 thunk!
delete operator由 vtable內offset資訊來算實際的derived object的位置.
----------
實際上的code flow:
最外層destructor call -> 最上層 destructor call
最後call delete destructor -> call 最外層bind到的operator delete

找要有virtual, 就會產生delete destructor(為virtual, 在base type的v-table就有一個slot), 所有
delete X;
都會經由delete destructor來call.
因為, 有可能
delete Base2;
v-table thunk將會給正確的Derived instance address.
注意!!
Base2 也須有virtual function! 不然
delete destructor將不會被喚起!!!!
----------------

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.