0
#include<iostream>
using namespace std;
class Person {
public:
    Person(int x) { cout << "Person::Person(int ) called" << endl; }
    //Person()   { cout << "Person::Person() called" << endl; }
};

class Faculty : virtual public Person {
public:
    Faculty(int x):Person(x) {
    cout<<"Faculty::Faculty(int ) called"<< endl;
    }
};

class Student : virtual public Person {
public:
    Student(int x):Person(x) {
        cout<<"Student::Student(int ) called"<< endl;
    }
};

class TA : public Faculty, public Student {
public:
    TA(int x):Student(x), Faculty(x),Person(x) {
        cout<<"TA::TA(int ) called"<< endl;
    }
};

int main() {
    cout<<"size Person "<<sizeof(Person)<<"\n";
    cout<<"size Faculty "<<sizeof(Faculty)<<"\n";
    cout<<"size Student "<<sizeof(Student)<<"\n";
    cout<<"size TA "<<sizeof(TA)<<"\n";
}

Output:

size of Person 1

size of Faculty 8
size of Student 8
size of TA 16

what internally happens in compiler? I think compiler definitely adds VPTR if it adds VPTR then is it assign to NULL?

for virtual destructor also compiler adds VPTR how compiler resolves everything internally ?

ganesh
  • 171
  • 1
  • 2
  • 13

1 Answers1

1

In C++ objects are addressable and therefore must have a size associated with them. In the case of Person there are no member variables, so there is no need for any space to be allocated for the instantiated object. However, since it must have a size the compiler gave it a size of 1.

If I add members to each of the classes we can then print the data in each object and see what is happening:

class Person {
public:
    Person(int x) {
        cout << "Person::Person(int ) called" << endl;
        y = 0xAAAAAAAAAAAAAAAA;
    }

    volatile unsigned long long int y;
};

class Faculty : virtual public Person {
public:
    Faculty(int x):Person(x) {
        cout<<"Faculty::Faculty(int ) called"<< endl;
        y = 0xBBBBBBBBBBBBBBBB;
    }

    volatile unsigned long long int y;
};

class Student : virtual public Person {
public:
    Student(int x):Person(x) {
        cout<<"Student::Student(int ) called"<< endl;
        y = 0xCCCCCCCCCCCCCCCC;
    }

    volatile unsigned long long int y;
};

class TA : public Faculty, public Student {
public:
    TA(int x):Person(x), Student(x), Faculty(x) {
        cout<<"TA::TA(int ) called"<< endl;
        y = 0xDDDDDDDDDDDDDDDD;
    }

    volatile unsigned long long int y;
};

By using the following function to print out the data allocated to an instantiated object of each class:

void print_obj(void* obj, unsigned size) {
    unsigned char * ptr = (unsigned char *)obj;
    for(unsigned i = 0; i < size; i++)
        printf("%02X", ptr[i]);
}

This is the output (gcc version 4.9.2):

Printing Person
AAAAAAAAAAAAAAAA

Printing Faculty
9814400000000000BBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA

Printing Student
7814400000000000CCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAA

Printing TA
D813400000000000BBBBBBBBBBBBBBBBF013400000000000CCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDAAAAAAAAAAAAAAAA

You can see that the members of each inherited class are present as well as a pointer for each inherited class (the bytes are out of order because the memory is little endian). I think it is safe to assume that the pointer points to a virtual method table for the inherited classes.

If you remove the class members, you end up with the VMT pointers and the class sizes become the same as those you listed in you question.

Alden
  • 2,229
  • 1
  • 15
  • 21