Home‎ > ‎

Inheritance example

Wikipedia has an article on virtual methods, containing an example in several languages.  On the right you can see the C++ version.

The purpose of the code (from Wikipedia):

A base class Animal could have a virtual function eat(). Subclass Fish would implement eat() differently than subclass Wolf, but you can invoke eat() on any class instance referred to as Animal, and get the eat() behavior of the specific subclass.

This allows a programmer to process a list of objects of class Animal, telling each in turn to eat (by calling eat()), with no knowledge of what kind of animal may be in the list. You also do not need to have knowledge of how each animal eats, or what the complete set of possible animal types might be.

This is how it is written in Zimbu:

CLASS Animal
  
PROC $eat() @default
    
IO.print("I eat like a generic Animal.")
  
}
}
 
CLASS Wolf EXTENDS Animal
 
PROC $eat() @replace
    
IO.print("I eat like a wolf!")
  
}
}
 
CLASS Fish EXTENDS Animal
  
PROC $eat() @replace @default
    
IO.print("I eat like a fish!")
  
}
}
 
CLASS GoldFish EXTENDS Fish @final
 
PROC $eat() @replace
    
IO.print("I eat like a goldfish!")
  
}
}
 
CLASS OtherAnimal EXTENDS Animal
}
 
FUNC Main() int
  
list<Animal> animals = NEW()
  animals.add(Animal.
NEW())
  animals.add(Wolf.
NEW())
  animals.add(Fish.
NEW())
  animals.add(GoldFish.
NEW())
  animals.add(OtherAnimal.
NEW())
 
  
FOR a IN animals
    a.eat()
  
}
  RETURN 0
}

This is the output:
I eat like a generic Animal.
I eat like a wolf!
I eat like a fish!
I eat like a goldfish!
I eat like a generic Animal.

Notes:
  • animals in Main() is a list of objects of type Animal.I, the interface of Animal.  All subclasses of Animal automatically implement this interface.  A list of Animal can only contain objects of Animal, not objects of a subclass.  This fixes a common misconception in object oriented programming: an object of a parent class may actually be an object of a subclass and this can't be avoided.
  • All methods are public by default.
  • All methods are only virtual when using the @default attribute, to indicate a method can be replaced in a subclass.  "virtual" is not a word with an obvious meaning, "default" is simpler to understand.
  • Replacing a default method requires using @replace attribute.  This avoids trouble when a method is added to the base class which was already present in a subclass.  The Zimbu compiler will give an error.  It would go unnoticed in C++ and Java.
  • Nothing needs to be imported, only standard items are used, such as the IO module, the compiler knows where these are.
  • The a in the FOR loop doesn't need to be declared, the compiler knows the type of the list items.  This avoids a declaration, which is very ugly in C++.
#include <iostream>
#include <vector>

using namespace std;
class Animal
{
public:
virtual void eat() const {
cout << "I eat like a generic Animal." << endl;
}
virtual ~Animal() {}
};

class Wolf : public Animal
{
public:
void eat() const {
cout << "I eat like a wolf!" << endl;
}

};

class Fish : public Animal
{
public:
void eat() const {
cout << "I eat like a fish!" << endl;
}

};

class GoldFish : public Fish
{
public:
void eat() const {
cout << "I eat like a goldfish!" << endl;
}
};


class OtherAnimal : public Animal
{
};

int main()
{
std::vector<Animal*> animals;
animals.push_back( new Animal() );
animals.push_back( new Wolf() );
animals.push_back( new Fish() );
animals.push_back( new GoldFish() );
animals.push_back( new OtherAnimal() );

for( std::vector<Animal*>::const_iterator it = animals.begin();
it != animals.end(); ++it)
{
(*it)->eat();
delete *it;
}

return 0;
}
Comments