0

This is work through sample of my class. To be honest, I know how the virtual works with the dynamic memory. But this is pretty hard to understand for me how this workthrough works. Can you guys please let me know how it works? In the case it is calling a.display(); foo(a);, I understand but I cannot understand how it calls function in horse when it calls animal function when it directly calls function in the horse.So I want general overview of this code, and since both are not that long, it won't take too long. This is header.

// Virtual Functions
// Animal.h

#include <iostream>

class Animal{
  public: 
   virtual void display() const;
};

class Horse : public Animal {
  public:
    void display() const;
};

This is implementation file.

// Virtual Functions
// Animal.cpp

#include "Animal.h"

void Animal::display() const {
  std::cout << "Animal" << std::endl;
}

void Horse::display() const{
  std::cout << "Horse" << std::endl;
}

and this is the first version.

// Virtual Functions - Monomorphic
// h16.m.cpp

#include "Animal.h"

void foo(const Animal a){
  a.display();
}
void goo(const Animal& a){
  a.display();
}

int main(){
  Animal a;
  Horse h;
  
  a.display();
  foo(a);
  goo(a);

  h.display();
  foo(h);
  goo(h);
}

and this is the second one.

// Virtual functions - Polymorphic
// h16.cpp

#include "Animal.h"

void foo(Animal a){
  a.display();
}

void goo(Animal& a){
  a.display();
}

void foogoo(Animal& a){
  a.Animal::display();
}

int main(){
  Animal* a; // Static typing
  a = new Animal(); // Dynamic typing
  a->display();  // ani
  foo(*a); //ani
  goo(*a);  //ani
  delete a;
  a = new Horse(); // Dynamic typing
  a->display(); //horse
  foo(*a); //ani
  goo(*a); //ani
  foogoo(*a); //ani
  delete a;
}
  • 4
    Does this answer your question? [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – Botje Aug 11 '20 at 08:17
  • In the first one, foo(h); this output is animal. is it because it foo does not take horse object as reference? Because it does not know the address of the object? – QuestionMaker Aug 11 '20 at 08:49
  • Because passing by copy slices the `Horse` into an `Animal`, and you thus revert to `Animal`. – Botje Aug 11 '20 at 08:51
  • I have one more question. I cannot understand how a->display(); could be horse. As I know since the function is not virtual it never allows function to use derived function. – QuestionMaker Aug 11 '20 at 09:09
  • Once a function is marked virtual in the base class, it is automatically virtual in derived classes as well. It cannot revert to non-virtual. You should also mark it `override` in subclasses so you get errors from the compiler if you ever change the signature. – Botje Aug 11 '20 at 09:11

1 Answers1

1

Virtual members have nothing to do with dynamic memory. They are related to accessibility of class members of object of class-type B via static type A while A is one of base classes of B.

Example:

struct A 
{
virtual void foo();
virtual void bar();
};

struct B : A
{
void foo() override;
};

struct C : B
{
void bar() override;
};

A *ptr = new B;
A& ref = *ptr;

Static (known at compile time) type of object pointed by ptr is A. new B expression is evaluated while program is running. It creates object of type B, hence dynamic type of object pointed by ptr is B. In its stead it is possible to write this with same result:

B  objB;
A* ptr = &B;   // value of address of variable saved to a pointer is run-time.
A& ref = *ptr;

ref is a reference to object with dynamic type B as well even if reference got static type A.

if foo() wasn't declared virtual, B::foo would be a method inaccessible though ptr or ref, because it is not present in A. But as it is, ptr->foo() will call B::foo().

bar() wasn't overridden in B , but was overridden in C. It's enough to be able to call C::bar() through referencing either A or B, because it was declared as virtual in A.

In your example, after a = new Horse();, any calls to virtual methods of class Animal accessed through derefrencing a are dispatched to Horse class if they were overridden there, otherwise to its parents. But in function

void foo(const Animal a){
  a.display();
}

argument isn't pointer or reference. It's a new variable of type Animal, which would receive copy of part of Horse which is Animal object. a.display(); here would access only Animal::display();

 Horse b;
 Animal a;
 a = b;  // it's only a part of Horse, it behaves like an Animal

This is exactly what happens when foo(h) is called. Such call includes assignment Animal a = h by definition of function call.

Such "cutting down" is referred as object slicing if it happens in accordance with compiler-declared operations (copy constructor or assignment). If such were redefined by user, transfer of content may be altered but destination object would retain its original static properties, including its original virtual table (a list of declared virtual members).

It is important to remember that class that got a base class, contains an object of base class. It is said that base class is a sub-object of derived class, just like any member declared withing Derived class.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • In the first one, foo(h); this output is animal. is it because it foo does not take horse object as reference? Because it does not know the address of the object? – QuestionMaker Aug 11 '20 at 08:49
  • @QuestionMaker yeah, you can fthink of it as not receiving address of Horse. that `foo()` received a copy of `Animal` object stored in `Horse`, see my edit. – Swift - Friday Pie Aug 11 '20 at 08:52