Parent directory
Makefile
inherit1.cpp
inherit2.cpp
inherit3.cpp
inherit4.cpp
inherit5.cpp
CC = g++
CXX = g++
CFLAGS = -g -Wall
CXXFLAGS = -g -Wall -std=c++20
executables = inherit1 inherit2 inherit3 inherit4 inherit5
.PHONY: default
default: $(executables)
inherit1: inherit1.o
inherit2: inherit2.o
inherit3: inherit3.o
inherit4: inherit4.o
inherit5: inherit5.o
.PHONY: clean
clean:
rm -f *~ a.out core *.o $(executables)
.PHONY: all
all: clean default
#include <iostream>
#include <cstdint>
#define w(expr) std::cout << #expr << ": " << expr << std::endl
class B {
public:
uint64_t sum() const { return b; }
// protected:
private:
uint64_t b = 100;
};
class D : public B {
public:
// uint64_t sum() const { return b + d; }
uint64_t sum() const { return B::sum() + d; }
private:
uint64_t d = 200;
};
int main() {
using namespace std;
// 1. Inheritance basics
D x;
w( x.sum() );
/*
// 2. Memory layout of derived classes
cout << "*** Object layout" << endl;
w( sizeof(x) );
uint64_t* p = (uint64_t*)&x;
// uint64_t* p = reinterpret_cast<uint64_t*>(&x);
w( p[0] );
w( p[1] );
// Use the following when we make sum() virtual:
// w( reinterpret_cast<void*>(p[0]) );
// w( p[1] );
// w( p[2] );
// 3. Static vs. dynamic binding
cout << "*** Pointer/reference to B can bind to a D object" << endl;
B* pb = &x;
D* pd = &x;
w( pb );
w( pd );
w( pb->sum() );
w( pd->sum() );
// 4. Virtual function table (vtable)
cout << "*** Calling a virtual function manually" << endl;
cout << ((uint64_t (***)(const D*))pb)[0][0](&x) << endl;
*/
}
#include <iostream>
#include <cstdint>
#include <cassert>
#define w(expr) cout << #expr << ": " << expr << endl
class B {
public:
uint64_t sum() const { return b; }
private:
uint64_t b = 100;
};
class C {
public:
uint64_t sum() const { return c; }
private:
uint64_t c = 150;
};
class D : public B, public C {
public:
uint64_t sum() const { return B::sum() + C::sum() + d; }
private:
uint64_t d = 200;
};
int main() {
using namespace std;
// 1. Multiple inheritance object layout
D x;
w( x.sum() );
cout << "*** Object layout" << endl;
w( sizeof(x) );
uint64_t* p = reinterpret_cast<uint64_t*>(&x);
w( p[0] );
w( p[1] );
w( p[2] );
/*
// 2. Base pointers have different addresses
cout << "*** Base pointers have different addresses" << endl;
B* pb = &x;
C* pc = &x;
D* pd = &x;
w( pb );
w( pc );
w( pd );
w( pb->sum() );
w( pc->sum() );
w( pd->sum() );
// 3. static_cast
cout << "*** static_cast" << endl;
{
// static_cast validates that you can downcast from C* to D*
D* pd2 = static_cast<D*>(pc);
w( pc );
w( pd2 );
// static_cast prevents you from casting to unrelated types
std::string s("hi");
// pd2 = static_cast<D*>(&s); // Compiler error
pd2 = (D*)&s; // No compiler error
// ...but static_cast isn't perfect
C y;
pd2 = static_cast<D*>(&y);
// w( pd2->sum() ); // Unpredictable result
}
// 4. Multiple inheritance with virtual functions
//
// Make these code changes and observe the output.
//
// 1) Add "virtual" to B::sum() and C::sum()
// 2) Add "override" to D::sum()
// 3) Write the following lines after "uint64_t* p = ..." in main():
//
// w( reinterpret_cast<void*>(p[0]) );
// w( p[1] );
// w( reinterpret_cast<void*>(p[2]) );
// w( p[3] );
// w( p[4] );
// 5. dynamic_cast
cout << "*** dynamic cast" << endl;
{
// dynamic_cast validates crosscasting and crosscasting
assert(dynamic_cast<B*>(pc) == pb);
assert(dynamic_cast<D*>(pc) == pd);
// ...and catches incorrect casts
C y;
C* pc2 = &y;
assert(dynamic_cast<B*>(pc2) == nullptr);
assert(dynamic_cast<D*>(pc2) == nullptr);
}
// 6. (Optional) vtable layout
{
cout << "*** vtable layout" << endl;
uint64_t* p = reinterpret_cast<uint64_t*>(&x);
void** vtbl = reinterpret_cast<void**>(p[0]);
w( reinterpret_cast<int64_t>(vtbl[-2]) );
w( vtbl[-1] );
w( vtbl[0] );
w( reinterpret_cast<int64_t>(vtbl[1]) );
w( vtbl[2] );
w( vtbl[3] );
// First vptr points to vtbl[0]
assert(reinterpret_cast<void**>(p[0]) == &vtbl[0]);
// Second vptr points to vtbl[3]
assert(reinterpret_cast<void**>(p[2]) == &vtbl[3]);
}
*/
}
#include <iostream>
#include <cstdint>
#define w(expr) cout << #expr << ": " << expr << endl
class A {
public:
virtual uint64_t sum() const { return a; }
private:
uint64_t a = 5;
};
class B : public A {
public:
virtual uint64_t sum() const { return b; }
private:
uint64_t b = 100;
};
class C : public A {
public:
virtual uint64_t sum() const { return c; }
private:
uint64_t c = 150;
};
class D : public B, public C {
public:
uint64_t sum() const override { return B::sum() + C::sum() + d; }
// A::sum() is ambiguous and does not compile:
// uint64_t sum() const override { return A::sum() + B::sum() + C::sum() + d; }
private:
uint64_t d = 200;
};
int main() {
using namespace std;
// 1. The diamond problem: D object contains two A portions
D x;
cout << "*** Object layout" << endl;
static_assert(sizeof(D) == 7 * sizeof(uint64_t));
uint64_t* p = reinterpret_cast<uint64_t*>(&x);
w( reinterpret_cast<void*>(p[0]) );
w( p[1] );
w( p[2] );
w( reinterpret_cast<void*>(p[3]) );
w( p[4] );
w( p[5] );
w( p[6] );
// 2. Base classes B & C still behave the same
cout << "*** Base pointers have different addresses" << endl;
B* pb = &x;
C* pc = &x;
D* pd = &x;
w( pb );
w( pc );
w( pd );
w( pb->sum() );
w( pc->sum() );
w( pd->sum() );
// 3. But referring to the base class A is ambiguous
//
// A* pa = &x;
}
#include <iostream>
#include <cstdint>
#include <cassert>
#define w(expr) cout << #expr << ": " << expr << endl
class A {
public:
virtual uint64_t sum() const { return a; }
private:
uint64_t a = 5;
};
class B : virtual public A {
public:
virtual uint64_t sum() const { return b; }
private:
uint64_t b = 100;
};
class C : virtual public A {
public:
virtual uint64_t sum() const { return c; }
private:
uint64_t c = 150;
};
class D : public B, public C {
public:
// There is now only one copy of A, so A::sum() is no longer ambiguous
uint64_t sum() const override { return A::sum() + B::sum() + C::sum() + d; }
private:
uint64_t d = 200;
};
int main() {
using namespace std;
// 1. Virtual inheritance fixes the diamond problem
D x;
cout << "*** Object layout" << endl;
static_assert(sizeof(D) == 7 * sizeof(uint64_t));
uint64_t* p = reinterpret_cast<uint64_t*>(&x);
w( reinterpret_cast<void*>(p[0]) );
w( p[1] );
w( reinterpret_cast<void*>(p[2]) );
w( p[3] );
w( p[4] );
w( reinterpret_cast<void*>(p[5]) );
w( p[6] );
// 2. sum() behaves polymorphically via any base pointer
cout << "*** Base pointers have different addresses" << endl;
A* pa = &x;
B* pb = &x;
C* pc = &x;
D* pd = &x;
w( pa );
w( pb );
w( pc );
w( pd );
w( pa->sum() );
w( pb->sum() );
w( pc->sum() );
w( pd->sum() );
// 3. (Optional) vtable layout
{
cout << "*** vtable layout" << endl;
uint64_t* p = reinterpret_cast<uint64_t*>(&x);
void** vtbl = reinterpret_cast<void**>(p[0]);
w( reinterpret_cast<int64_t>(vtbl[-3]) );
w( reinterpret_cast<int64_t>(vtbl[-2]) );
w( vtbl[-1] );
w( vtbl[0] );
w( reinterpret_cast<int64_t>(vtbl[1]) );
w( reinterpret_cast<int64_t>(vtbl[2]) );
w( vtbl[3] );
w( vtbl[4] );
w( reinterpret_cast<int64_t>(vtbl[5]) );
w( reinterpret_cast<int64_t>(vtbl[6]) );
w( vtbl[7] );
w( vtbl[8] );
// First vptr points to vtbl[0]
assert(reinterpret_cast<void**>(p[0]) == &vtbl[0]);
// Second vptr points to vtbl[4]
assert(reinterpret_cast<void**>(p[2]) == &vtbl[4]);
// Third vptr points to vtbl[8]
assert(reinterpret_cast<void**>(p[5]) == &vtbl[8]);
}
}
#include <iostream>
#include <cstdint>
using namespace std;
#define w(expr) cout << #expr << ": " << expr << endl;
struct M {
M(uint64_t x) : m{x} { cout << "M::M(uint64_t)" << endl; }
~M() { cout << "M::~M()" << endl; }
uint64_t m;
};
struct B {
B(uint64_t x) : b{x} { cout << "B::B(uint64_t)" << endl; }
~B() { cout << "B::~B()" << endl; }
// virtual uint64_t sum() const = 0;
uint64_t b;
};
struct D : public B {
D() : B{100}, m_obj{200}, d{300} { cout << "D::D()" << endl; }
~D() { cout << "D::~D()" << endl; }
// uint64_t sum() const override { return b + m_obj.m + d; }
M m_obj;
uint64_t d;
};
int main() {
cout << "*** Order of construction and destruction" << endl;
{
D x;
// B y; // Compilation error
// B* bp = &x;
// w( bp->sum() );
}
// cout << "*** Virtual destructor" << endl;
// Observe the change in output when you:
// - change B::~B() to virtual
// - override in D::~D().
// B* pb = new D;
// delete pb;
}