3

I have a class Student which inherits from Person. Both classes just have the defined virtual function printDetails() and their constructors.

Person student = Student("John Smith", "a1234567");
student.printDetails();

When i have the code above in my main the printDetails() function is called in the Person class.

Person* student = new Student("John Smith", "a1234567")
student->printDetails()

However when i have the dynamically allocated version above the function is called by the student.

My question is, why is printDetails() being called in the Person class rather then in the Student one for the first segment of code?

3 Answers3

7
Person student = Student("John Smith", "a1234567");

Your student isn't a Student. It is a Person. You declared it as such. So of course the function that is called is Person::printDetails.

What's happening is called "slicing". See this FAQ for details: What is object slicing? .

Update
This answers roybatty's question "Why does the second case call Student::printDetails?"

The first case loses all details that the object is a Student. That's just what happens when one assigns a child class to a parent class. The parent class object doesn't know of the member data that the child class adds, or of the functions that the child class overrides. How can it?

That's not the case in the second case. Here it's a pointer that is being upcast to a parent class pointer. There's still a Student object that is intimately connected to the Parent* pointer, even though that pointer is a base class pointer. There would be no point in declaring a member function virtual if that connection to the derived class didn't exist.

Community
  • 1
  • 1
David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • Nice use of the ninja edit to add the reference to *slicing*. – Mark Ransom Nov 18 '13 at 02:52
  • Good observation, easy to see why student is a Person. But how does this explain why an instance of Person* calls the dynamically allocated version of printDetails() from the Student class? That is, if I'm correctly understanding what the OP described (2nd case). – Bruce Dean Nov 18 '13 at 03:00
  • @roybatty - In the second case the object retains its full identity as a `Student`. There's no slicing here; it's just the pointer that has been upcast to a `Parent*`. The runtime environment must use dynamic dispatch on the call to `student->printDetails()` because `Person::printDetails` is declared as virtual. – David Hammen Nov 18 '13 at 03:32
  • @MarkRansom - I didn't steal from your answer, if that's what you're hinting. – David Hammen Nov 18 '13 at 03:35
  • 1
    No, I wasn't hinting that at all, it was a sincere compliment. In fact if that information had been in your original answer I wouldn't have bothered with my own. I'm not vain enough to think I'm the only one who knows about it. – Mark Ransom Nov 18 '13 at 03:43
  • @MarkRansom - Oh. Thanks. Here's a brief history of how I wrote my answer. I wrote the first part and thought "There's a name for this! What is it?" I couldn't remember, so I submitted my answer and opened it for editing. Then I searched for the name. It didn't take long to find that the word I was looking for was "slicing". Then I searched here to find that FAQ. – David Hammen Nov 18 '13 at 04:07
1

You're a victim of what's called object slicing. The object student is of type Person, but it was created with the copy constructor of Person applied to a Student object. Slicing means that all the extra attributes of being a Student are lost.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
0

To understand the problem, you must know how the objects Person and Student is Laid out in memory.

Consider the classes

class Person
{
protected:
    int age;

public:
    Person(int a)
    {
        age = a;
    }
    virtual void Print()
    {
        cout<<"Person Age : "<<age;
    }
};

class Student : public Person 
{
    int Id;

public:
    Student(int id, int age)
        :Person(age)
    {
        Id = id;
    }
    virtual void Print()
    {
        cout<<"Student Age : "<<age<<" Id : "<<Id;
    }
};

Following is the object layout Person and Student objects

-----------------------------------------------------
|_Virtual_Table_Ptr_Person | Data members of Person |
-----------------------------------------------------
-------------------------------------------------------------------------------
|_Virtual_Table_Ptr_Student | Data members of Person | Data Members of Student|
-------------------------------------------------------------------------------

So, For the statement Person student = Student() , The memory of temp object Student() gets copied over to the memory address of student. So the object gets sliced here. The _Vptr however remains the same (i.e VT of Person).

SysAdmin
  • 5,455
  • 8
  • 33
  • 34