C++ multiple level inheritance thunk object pointer adjustment - c++

Consider the following code with 3-level multiple inheritance hierachy.
auto addr = [](auto v) -> uint64_t { return *reinterpret_cast<uint64_t*>(v); };
struct BaseA
{
void virtual a() {}
};
struct BaseB
{
void virtual b() {}
};
struct BaseC : BaseA, BaseB
{
void virtual a() override {}
void virtual b() override {}
};
struct BaseD
{
void virtual d() {}
};
struct BaseE : BaseD, BaseC
{
void virtual d() override {}
void virtual a() override {}
void virtual b() override { auto a = this; std::cout << "called here: " << addr(&a) << "\n"; }
};
int main()
{
BaseE obj;
BaseE* ePtr = &obj;
BaseD* dPtr = &obj;
BaseC* cPtr = &obj;
BaseB* bPtr = &obj;
BaseA* aPtr = &obj;
ePtr->b();
cPtr->b();
bPtr->b();
std::cout << "e is at: " << addr(&ePtr) << "\n"
<< "d is at: " << addr(&dPtr) << "\n"
<< "a is at: " << addr(&aPtr) << "\n"
<< "c is at: " << addr(&cPtr) << "\n"
<< "b is at: " << addr(&bPtr) << "\n"
<< "total size is " << sizeof(BaseE) << "\n"
<< "vptr D and E " << vpt1 << "\n"
<< "vptr A and C " << vpt2 << "\n"
<< "vptr B " << vpt3 << "\n";
return 0;
}
The output from this code run is the following:
called here: 140736308965696
called here: 140736308965696
called here: 140736308965696
e is at: 140736308965696
d is at: 140736308965696
a is at: 140736308965704
c is at: 140736308965704
b is at: 140736308965712
total size is 24
vptr D and E 4390608
vptr A and C 4390648
vptr B 4390680
This suggest the following memory layout for BaseE obj (with the pointer size being 8 bytes).
8 bytes, BaseD subobject, vptr to D table only
8 bytes, BaseA subobject, vptr to A table only
8 bytes, BaseB subobject, vptr to B table only.
Here ePtr and dPtr both point to BaseD subobject, both aPtr and cPtr point to BaseA subobject and bPtr to BaseB subobject.
My question is now is what thunk code will the compiler generate to adjust the this pointer in two calls above to b() through pointers cPtr and bPtr to make sure this points correctly to ePtr when BaseE implementation of b() is called? Since cPtr and bPtr have different address does the thunk need to be aware to adjust differently based on what type of Base class pointer is passed to it?

Related

C++ virtual function call by value

I attempt to modify the vfptr of a Base object to point to vtable which class Derived owns:
#include<iostream>
using namespace std;
class Base {
public:
virtual void func() { cout << "Base::func()" << endl; }
Base() = default;
Base(const Base&);
};
Base::Base(const Base&b) {
*((int*)(this)) = *((int*)(&b)); //modify the vfptr
}
class Derived : public Base {
public:
virtual void func() { cout << "Derived::func()" << endl; }
};
int main() {
Derived d;
cout << "Derived's vtable: " << *(int*)&d << endl << endl;
Base b = d;
cout << "Base's vtable: " << *(int*)&b << endl << endl;
cout << "pass by value:" << endl;
b.func();
cout << endl;
Base *bp = &b;
cout << "pass by pointer" << endl;
bp->func();
}
Output in VS2017:
Derived's vtable: 9739088
Base's vtable: 9739088
pass by value:
Base::func()
pass by pointer
Derived::func()
We can see that vfptr of object b does point to the Derived vtable after modifing but still call the Base version func() by value.
I wonder if the compiler handles such value-call by static binding even if what is called is a virtual function?
Thank you so much.
I wonder if the compiler handles such value-call by static binding
Base b = d;
b.func();
Yes, it does do static binding here indeed. In fact, using dynamic binding would be pointless because the dynamic type of the object is known to be the same as the static type at compile time.

How to get virtual destructors to be called in C++?

I am trying to see the effects of calling virtual destructors of classes belonging to a long chain of hierarchy: class A to class E.
Strangely, the destructors do not write anything to the console. I first thought perhaps it was happening because main was exiting too. So, I put all the testing code within a function called test() and invoked from within main() so when test would return, I would see destructor footprints. But, nothing! No "cout" signs on the console show up!
#include <iostream>
using namespace std;
//A constructor cannot be virtual but a destructor can.
class A {
public:
A() {
cout << "A constructor" << endl;
}
virtual ~A() {cout << "A destructor" << endl;}
};
class B :public A {
public:
B() {
cout << "B constructor" << endl;
}
virtual ~B() {cout << "B destructor" << endl;}
};
class C :public B {
public:
C() {
cout << "C constructor" << endl;
}
virtual ~C() {cout << "C destructor" << endl;}
};
class D :public C {
public:
D() {
cout << "D constructor" << endl;
}
~D() {cout << "D destructor" << endl;}
};
class E :public D {
public:
E() {
cout << "E constructor" << endl;
}
~E() {cout << "E destructor" << endl;}
};
void test() {
cout << "Test1 begins..." << endl;
A* a1 = new D();
cout << "Test2 begins..." << endl;
A* a2 = new E();
}
int main() {
test();
return 0;
}
Ummm... you actually leak those.
Every objected created by the new keyword must have an equivilant delete:
void test() {
cout << "Test1 begins..." << endl;
A* a1 = new D();
cout << "Test2 begins..." << endl;
A* a2 = new E();
delete a1;
delete a2;
}
Developers (just in your case) always forgot to delete dynamically allocated objects, so smart pointers were introduce:
void test() {
cout << "Test1 begins..." << endl;
std::unique_ptr<A> a1(new D());
cout << "Test2 begins..." << endl;
std::unique_ptr<A> a2(new E());
}
no need to worry about a leak, as unique_ptr automatically delete their pointee when they get out of scope.
You never delete your raw pointers. Prefer smart pointers to raw ones.
You should add
delete a1;
delete a2;
near the end of your test.
Try also to create some instances of E as an automatic variable (usually on the call stack). For example, insert
E ee;
in between those two delete-s.

Something I can not figure out about Vtable and Vptr in Multiple Inheritance

The following code
class B
{
public:
void f1() {}
};
class C
{
public:
void f2() {}
};
class D : public B, public C
{
public:
virtual void f3() {}
};
int main()
{
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
cout << sizeof(D) << endl;
system("pause");
return 0;
}
gets a result 1 1 8.So why is 8 not 4(in my computer,a pointer takes 4 bytes)?
B,C do not have vptr, and when D has virtual members, D's Vptr is placed in the Vtable of the first inherited class which is B,since the following code
class B
{
public:
virtual void f1() {} //now is virtual
};
class C
{
public:
void f2() {}
};
class D : public B, public C
{
public:
virtual void f3() {}
};
int main()
{
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
cout << sizeof(D) << endl;
system("pause");
return 0;
}
gets a result 4,1,4. May somebody explain it to me, thanks a lot!

Why is this the output of this program?

#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A ctor" << endl;
}
virtual ~A()
{
cout << "A dtor" << endl;
}
virtual void foo() = 0;
};
class B : public A
{
public:
B()
{
cout << "B ctor" << endl;
}
virtual ~B()
{
cout << "B dtor" << endl;
}
virtual void foo()
{
cout <<"B's foo" << endl;
}
};
class C : public A
{
public:
C() {
cout << "C ctor" << endl;
}
virtual ~C()
{
cout << "C dtor" << endl;
}
virtual void foo() {cout << "C's foo" << endl;
}
};
int main ()
{
C *ptr = new C[1];
B b;
return 0;
}
This gives the following output:
A ctor
C ctor
A ctor
B ctor
B dtor
A dtor
I don't understand why this is happening. For example, I know that a new C object is being created, that's derived from A, so the A ctor runs first. Then the C ctor runs. And then I thought the C dtor runs, but for some reason the A ctor is running again.
C is created, this constructs A (base class) and then C
B is created, this constructs A (base class) and then B
B is destroyed (goes out of scope), this destructs B and then A (base class)
C is never deleted, so it's leaked and the destructors are never called.

C++ Virtual Destructors in a 4 level inheritance chain.

I was doing a little experiment with virtual destructors to review - wondering if anyone has a simple explanation for the following (using vs 2010):
I Define class hierarchy A-B-C-D, D inherits C, C inherits B, B inherits A, A is the Base;
ran 2 experiments:
First experiment -
A has a virtual Destructor.
B has a non-Virtual Destructor
C has a virtual Destructor
D has a non virtual Destructor
//----------------------------
Allocate 4 objects on the heap of type D - Point a pointer of A*, B* and C* at the first 3 - Leave the 4th as a D* for Completeness.
Delete all 4 Pointers.
As I expected, in all 4 instances, the complete destructor chain is executed in reverse order from D down to A, freeing all memory.
Second Experiment -
A has a non-virtual Destructor ** Changed A to non virtual
B has a non-Virtual Destructor
C has a virtual Destructor
D has a non virtual Distructor
Allocate 4 objects on the heap of type D - Point a pointer of A*, B*, and C* at the first 3 - Leave the 4th as a D* for Completeness.
Deleting C* and D* pointers:
the complete destructor chain is executed in reverse order from D down to A, freeing all memory.
Deleting B*:
B and then A Destructor is run (leak)
Deleting A*:
Only A Destructor is run (leak)
Can anyone explain Why this is?
When D type opjects are allocated in experiment 2, its immediate base class (C) has a virtual destructor - doesnt that tell the compiler to track it with a Vptr and know the memory type? REGARDLESS of the reference?
Thanks
Mike
When D type opjects are allocated in experiment 2, its immediate base class (C) has a virtual destructor - doesnt that tell the compiler to track it with a Vptr and know the memory type? REGARDLESS of the reference?
No.
In your second test case, A and B don't have vptrs/vtables. (And even if they did, a non-virtual member function would still be resolved statically, not dynamically.)
Put another way, a base class does not "inherit" information (such as whether functions are virtual) from derived classes.
When you delete an A* without a virtual destructor, at compile-time the compiler does not know that it will point at runtime to an object with a virtual destructor. The deletion might be of an object with a virtual destructor -- or not. Dynamic binding does not occur.
Is your actual question about why one would use virtual vs non-virtual destructors? Cos having a base class with a non-virtual destructor is bad. See the faq
I have composed almost identical question, so I thought to share it.
Note that I also added some usage of virtual function within the different Ctor's in order to illustrate how it is work (shortly, in each Ctor, the V-table is updated only "up to it" , meaning the virtual function implementation that will be invoked is the most derived until "this point" of the inheritance chain).
A note of mine: In the sample code that runs the given classes, I also added a creation of "Derived" object (B and D) on the STACK --> in order to emphasis that all the considerations regarding "virtual-ness" of Dtor's are applicable when we use pointers (of whatever type) to the class instance.
class A;
void callBack(A const& a);
class A
{
public:
A() { std::cout << "A Ctor " << std::endl; f1(); callBack(*this); /* f3();*/ }
~A() { std::cout << "A Dtor " << std::endl; }
void f1() { std::cout << "A : f1 " << std::endl; }
virtual void f2() const { std::cout << "A : f2 " << std::endl; }
virtual void f3() = 0;
};
class B : public A
{
public:
B() { std::cout << "B Ctor " << std::endl; f1(); callBack(*this); f3(); }
~B() { std::cout << "B Dtor " << std::endl; }
void f1 () { std::cout << "B : f1 " << std::endl;}
void f2() const { std::cout << "B : f2 " << std::endl; }
virtual void f3() { std::cout << "B : f3 " << std::endl; }
};
class C : public A
{
public:
C() { std::cout << "C Ctor " << std::endl; f1(); callBack(*this); f3(); }
virtual ~C() { std::cout << "C Dtor " << std::endl; }
void f1() { std::cout << "C : f1" << std::endl;}
void f2() const { std::cout << "C : f2" << std::endl; }
virtual void f3() const { std::cout << "C : f3" << std::endl; }
};
class D : public C
{
public:
D() { std::cout << "D Ctor " << std::endl; f1(); callBack(*this); }
~D() { std::cout << "D Dtor " << std::endl; }
void f1() { std::cout << "D : f1" << std::endl; }
void f2() const { std::cout << "D : f2 " << std::endl; }
virtual void f3() { std::cout << "D : f3 " << std::endl; }
};
void callBack(A const& a) { a.f2(); }
// =================================================================================================================================
int main()
{
std::cout << "Start of main program" << std::endl;
std::cout << "Creating a D object on the heap" << std::endl;
D* pd = new D;
C* pc = new D;
A* pa = new D;
if (true)
{
std::cout << "Entering Dummy scope # 1 and creating B object on the stack" << std::endl;
B b;
std::cout << "Leaving Dummy scope # 1 with B object within it" << std::endl;
}
if (true)
{
std::cout << "Entering Dummy scope # 2 and creating D object on the stack" << std::endl;
D d;
std::cout << "Leaving Dummy scope # 2 with D object within it" << std::endl;
}
std::cout << "Calling delete on pd (D*) which points on a D object" << std::endl;
delete pd;
std::cout << "Calling delete on pc (C*) which points on a D object" << std::endl;
delete pc;
std::cout << "Calling delete on pa (A*) which points on a D object" << std::endl;
delete pa;
std::cout << "End of main program" << std::endl;
return 0;
}