Dynamic Binding: Class-Appropriate behavior

Steven Zeil

Last modified: Feb 24, 2014

1. Dynamic Binding

Dynamic binding is a key factor in allowing different classes to respond to the same message with different methods.

1.1 What is dynamic binding?

Binding is a term used in programming languages to denote the association of information with a symbol. It’s a very general term with many applications.

Binding Function Calls to Bodies

In OOP, we are particularly interested in the
binding of a function body (method) to a function call.

Given the following:

  a = foo(b);

When is the decision made as to what code will be executed for this call to foo?

Compile-Time Binding

  a = foo(b);

In traditionally compiled languages (FORTRAN, PASCAL, C, etc), the decision is made at compile-time.

Run-Time Binding

  a = foo(b);

In traditionally interpreted languages (LISP, BASIC, etc), the decision is made at run-time.

Dynamic Binding = the Happy Medium?

OOPLs typically feature dynamic binding, an “intermediate” choice in which

Dynamic Binding and Programming Languages

1.2 Dynamic Binding in C++

Virtual functions

Dynamic Binding in C++: virtual functions

2. An Example of Dynamic Binding

An Animal Inheritance Hierarchy

For this example, we will introduce a simple hierarchy.

class Animal {
public:
  virtual String eats() {return "???";}
  String name() {return "Animal";}
};          

We begin with the base class, Animal, which has two functions.

Plant Eaters

Now we introduce a subclass of Animal that overrides both those functions.

class Herbivore: public Animal {
public:
   virtual String eats() {return "plants";}
   String name() {return "Herbivore";}
};

Cud-Chewers

Now we introduce a subclass of that class.

class Ruminants: public Herbivore {
public:
   virtual String eats() {return "grass";}
   String name() {return "Ruminant";}
};          

Meat Eaters

And another subclass of the original base class.

class Carnivore: public Animal {
public:
   virtual String eats() {return "meat";}
   String name() {return "Carnivore";}
};          

Output Function

It will also be useful in this example to have a simple utility function to print a pair of strings.

void show (String s1, String s2) {
          cout << s1 << " " << s2 << endl;
}          

Let’s Make Some Calls

 Animal a, *paa, *pah, *par;
 Herbivore h, *phh;
 Ruminant r;
 paa = &a; phh = &h; pah = &h; par = &r;
 
 show(a.name(), a.eats());      // AHRC ?pgm
 show(paa->name(), paa->eats()); // AHRC ?pgm
 show(h.name(), h.eats);        // AHRC ?pgm
 show(phh->name(), phh->eats()); // AHRC ?pgm
 show(pah->name(), pah->eats()); // AHRC ?pgm
 show(par->name(), par->eats()); //AHRC ?pgm

Note the variety of variables we are using.

What’s the output?

 Animal a, *paa, *pah, *par;
 Herbivore h, *phh;
 Ruminant r;
 paa = &a; phh = &h; pah = &h; par = &r;
 
 show(a.name(), a.eats());      // AHRC ?pgm
 show(paa->name(), paa->eats()); // AHRC ?pgm
 show(h.name(), h.eats);        // AHRC ?pgm
 show(phh->name(), phh->eats()); // AHRC ?pgm
 show(pah->name(), pah->eats()); // AHRC ?pgm
 show(par->name(), par->eats()); //AHRC ?pgm

Question: What will be the output of the various show calls?

Answer:

3. Why is Dynamic Binding Important?

Dynamic binding lets us write application code for the superclass that can be applied to the subclasses, taking advantage of the subclasses’ different methods.

3.1 The Key Pattern to All OOP

Collections of Pointers/References to a Base Class

Suppose we have an inheritance hierarchy:

and that we have a collection of pointers or references to the BaseClass.

The Key Pattern to All OOP

Then this code:

BaseClass* x;
for (each x in collection) {
   x->virtualFunction(...);
}

uses dynamic binding to apply subclass-appropriate behavior to each element of a collection.

3.2 Examples of the key pattern

There are lots of variations on this pattern. We can use almost any data structure for the collection.

Example: arrays of Animals

 Animal** animals = new Animal*[numberOfAnimals];
    ⋮
 for (int i = 0; i < numberOfAnimals; ++i)
    cout << animals[i]->name() << " "
        << animals[i]->eats() << endl;

Example: Linked Lists of Animals (C++)

 struct ListNode {
    Animal* data;
    ListNode* next;
 };
 ListNode* head; // start of list
    ⋮
 for (ListNode* current = head; current != 0; current = current->next)
    cout << current->data->name() << " "
         << current->data->eats() << endl;

Example: vector of Animals

 vector<Animal*> animals;
    ⋮
 for (int i = 0; i < animals.size(); ++i)
    cout << animals[i]->name() << " "
         << animals[i]->eats() << endl;

Example: Trees of Animals

 struct TreeNode {
    Animal* data;
    TreeNode* leftChild;
    TreeNode* rightChild;
 };
 TreeNode* root;
 
 void printTree (const TreeNode* t)
 {
   if (t != 0) {
     printTree(t->leftChild);
     cout << t->data->name() << " "
          << t->data->eats() << endl;
     printTree(t->rightChild);
   }
 }
    ⋮
 printTree(root);

4. Examples

4.1 Example: Spreadsheet – Rendering Values

Continuing our earlier example:

value.h

Displaying a Cell

Here is the code to draw a spreadsheet on the screen.

 void NCursesSpreadSheetView::redraw() const
 {
   drawColumnLabels();
   drawRowLabels();
 
   CellRange shown = showing();
   for (CellName cn = shown.first();
        shown.more(cn); cn = shown.next(cn))
     drawCell(cn);
 }

We have a loop that goes through the collection of cell names, invoking drawCell on each one.

drawCell

 void NCursesSpreadSheetView::drawCell
     (CellName name) const
 {
   string cellValue;
   Cell* c = sheet.getCell(name);
   const Value* v = c->getValue();
   if (v != 0)
    {
     cellValue = v->render(theColWidth);
    }
   centerStringInWidth (cellValue,
                         theColWidth);
   // . . . show cellValue on screen . . .
 }

render()

Now render in value.h is virtual, and various bodies implementing it can be found in classes like

numrender.cpp

, NumericValue,

strrender.cpp

, StringValue, and

errrender.cpp

, ErrorValue, .

4.2 Example: Evaluating a Cell

celleval.cpp

operator==

Look at the implementation of operator== in value.h.

numeq.cpp

,

streq.cpp

, and

erreq.cpp

.