 Parent directory
Parent directory
 Makefile
Makefile
 inherit1.cpp
inherit1.cpp
 inherit2.cpp
inherit2.cpp
 inherit3.cpp
inherit3.cpp
 inherit4.cpp
inherit4.cpp
 inherit5.cpp
inherit5.cpp
CC  = g++
CXX = g++
CFLAGS   = -g -Wall
CXXFLAGS = -g -Wall -std=c++17
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>
#define  w(expr)  std::cout << #expr << ": " << expr << std::endl
class B {
public:
    double sum() const { return b; }
//protected:
private:
    double b = 100.0;
};
class D : public B {
public:
    //double sum() const { return b + d; }
    double sum() const { return B::sum() + d; }
private:
    double d = 200.0;
};
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) );
    double *p = (double *)&x;
    w( p[0] );
    w( p[1] );
    // 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 << ((double (***)(const D*))pb)[0][0](&x) << endl;
    */
}
/*  Lecture outline
1.  Inheritance basics
    - default member initializer
    - B::sum() - invoking the base class's member function
    - w() macros for convenience
    - protected access modifier
2.  Memory layout of derived classes
3.  Static vs. dynamic binding
    - Pointer or reference to B can bind to a D object
    - Static binding by default unlike Java
    - Dynamic binding requries "virtual" function specifier:
        virtual double sum() const { return b; }
    - "override" specifier ensures that the function is virtual and is
      overriding a virtual function from a base class:
        double sum() const override { return B::sum() + d; }
4.  Virtual function table (vtable)
    - Object size & layout changes when it contains virtual functions
    - Diagram of vtable and vtable pointer
    - Calling a virtual function manually
*/#include <iostream>
#define  w(expr)  cout << #expr << ": " << expr << endl
class B {
public:
    double sum() const { return b; }
private:
    double b = 100.0;
};
class C {
public:
    double sum() const { return c; }
private:
    double c = 150.0;
};
class D : public B, public C {
public:
    double sum() const { return B::sum() + C::sum() + d; }
private:
    double d = 200.0;
};
int main() {
    using namespace std;
    // 1.  Multiple inheritance object layout
    D x;
    w( x.sum() );
    cout << "*** Object layout" << endl;
    w( sizeof(x) );
    double *p = (double *)&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.  Multiple inheritance with virtual functions
    //
    //     - Make the code change (below) and observe the output.
}
/*  Lecture outline
1.  Multiple inheritance object layout
    - Multiple inheritance diagram
2.  Base pointers have different addresses
    - The compiler must change the memory addresses of base-type pointers
      to the same multiply derived object in order for the base-type pointers
      to point to the right part of the object.
3.  Multiple inheritance with virtual functions
    - Change code as follows:
        1) Add "virtual" to B::sum() and C::sum()
        2) Add "override" to D::sum()
        3) Add the follong lines after "w(p[2]);" in main():
            w( p[3] );
            w( p[4] );
            cout << "*** p[0] and p[2] hold vtable pointers (vptr)" << endl;
            w( *(void **)&p[0] );
            w( *(void **)&p[2] );
    - pb->sum() and pc->sum() polymorphically invoke D::sum()
    - Two vptrs are added to the object
*/#include <iostream>
#define  w(expr)  cout << #expr << ": " << expr << endl
class A {
public:
    virtual double sum() const { return a; }
private:
    double a = 5.0;
};
class B : public A {
public:
    virtual double sum() const { return b; }
private:
    double b = 100.0;
};
class C : public A {
public:
    virtual double sum() const { return c; }
private:
    double c = 150.0;
};
class D : public B, public C {
public:
    double sum() const override { return B::sum() + C::sum() + d; }
    // A::sum() is ambiguous and does not compile:
    //
    // double sum() const override { return A::sum() + B::sum() + C::sum() + d; }
private:
    double d = 200.0;
};
int main() {
    using namespace std;
    // 1.  The diamond problem: D object contains two A portions
    D x;
    cout << "*** Object layout" << endl;
    w( sizeof(x) );
    double *p = (double *)&x;
    for (size_t i = 0; i < sizeof(x) / 8; ++i) {
        cout << "p[" << i << "]: " << p[i] << endl;
    }
    // 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;
}
/*  Lecture outline
1.  The diamond problem: D object contains two A portions
2.  Base classes B & C still behave the same
3.  But referring to the base class A is ambiguous
*/#include <iostream>
#include <cassert>
#define  w(expr)  cout << #expr << ": " <<         expr << endl
#define  v(expr)  cout << #expr << ": " << (void *)expr << endl
class A {
public:
    virtual double sum() const { return a; }
private:
    double a = 5.0;
};
class B : virtual public A {
public:
    virtual double sum() const { return b; }
private:
    double b = 100.0;
};
class C : virtual public A {
public:
    virtual double sum() const { return c; }
private:
    double c = 150.0;
};
class D : public B, public C {
public:
    // There is now only one copy of A, so A::sum() is no longer ambiguous
    double sum() const override { return A::sum() + B::sum() + C::sum() + d; }
private:
    double d = 200.0;
};
int main() {
    using namespace std;
    // 1.  Virtual inheritance fixes the diamond problem
    D x;
    cout << "*** Object layout" << endl;
    w( sizeof(x) );
    double *p = (double *)&x;
    for (size_t i = 0; i < sizeof(x) / 8; ++i) {
        cout << "p[" << i << "]: " << p[i] << endl;
    }
    // 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] D's vtable layout
    cout << "*** p[0],p[2],p[5] point to different parts of D's vtable" << endl;
    int64_t *vptr[3];
    vptr[0] = *(int64_t **)&p[0];  v(vptr[0]);
    vptr[1] = *(int64_t **)&p[2];  v(vptr[1]);  assert(vptr[1]-vptr[0]==4);
    vptr[2] = *(int64_t **)&p[5];  v(vptr[2]);  assert(vptr[2]-vptr[1]==4);
    for (size_t i = 0; i < 3; ++i) {
        cout << "\n";
        cout << "vbase/vcall offset: " <<         vptr[i][-3] << endl;
        cout << "        top offset: " <<         vptr[i][-2] << endl;
        cout << "  typeinfo pointer: " << (void *)vptr[i][-1] << endl;
        cout << "  pointer to sum(): " << (void *)vptr[i][0]  << endl;
        
        cout << "calling sum() with right part of x: ";
        double (*f)(void *);  // declare a pointer to sum() function
        f = (double (*)(void *)) vptr[i][0];
        char *part_of_x = (char *)&x - vptr[i][-2];  // point to the right part
        cout << f( part_of_x ) << endl;
    }
    */
}
/*  Lecture outline
1.  Virtual inheritance fixes the diamond problem
2.  sum() behaves polymorphically via any base pointer
3.  [Optional] D's vtable layout
    - Reference:
        https://nimrod.blog/posts/what-does-cpp-object-layout-look-like/
*/#include <iostream>
using namespace std;
#define  w(expr)  cout << #expr << ": " << expr << endl;
struct S1 : public string {
    S1(const char *s = "") : string{s} { cout << "S1::S1()" << endl; }
    ~S1() { cout << "S1::~S1()" << endl; }
};
struct S2 : public string {
    S2(const char *s = "") : string{s} { cout << "S2::S2()" << endl; }
    ~S2() { cout << "S2::~S2()" << endl; }
};
class B {
public:
    B() { cout << "B::B()" << endl; }
    B(double x) : b{x} { cout << "B::B(double)" << endl; }
    ~B() { cout << "B::~B()" << endl; }
    double sum() const { return b; }
private:
    double b = 100.0;
};
class C {
public:
    C() { cout << "C::C()" << endl; }
    C(double x) : c{x} { cout << "C::C(double)" << endl; }
    ~C() { cout << "C::~C()" << endl; }
    double sum() const { return c; }
private:
    double c = 150.0;
};
class D : public B, public C {
public:
    D() : B{99}, C{149}, s1{"abc"}, s2{"xyz"} {
        cout << "D::D()" << endl; 
    }
    ~D() { 
        cout << "D::~D()" << endl; 
    }
    double sum() const { return B::sum() + C::sum() + d; }
private:
    double d = 200.0;
    S1 s1;
    S2 s2;
};
int main() {
    D x;
    w( x.sum() )
    w( sizeof(x) )
}
/*  Lecture outline
1.  Order of initialization: base classes, data members, contructor body
    - Note the base class initialization syntaxt: B{99}
    - Explicit initialization of data member overrides the default initializer
    - Destructors are called in the opposite order
*/