Revisions:
Nov 7, 2007: added section on combining static and dynamic binding
C++ uses Static binding by default, dynamic binding is activiated by defining methods as virtual.
class SavingAccount : public BankAccount {
public:
SavingAccount(int acctnum) :
BankAccount(acctnum)
{}
void printOn(ostream & o) {o <<
"SavingAcct #" << accountNumber << "\n";}
};
void main() {
BankAccount b(1000);
SavingAccount s(1001);
cout << b << s;
}
BankAccount #1000
BankAccount #1001
class SavingAccount : public BankAccount {
public:
SavingAccount(int acctnum) :
BankAccount(acctnum)
{}
void printOn(ostream & o) {o <<
"SavingAcct #" << accountNumber << "\n";}
};
void main() {
BankAccount b(1000);
SavingAccount s(1001);
cout << b << s;
}
BankAccount #1000
SavingAcct #1001
C++ uses pure virtual functions to define abstract classes. You cannot create an object from an abstract class, only concrete subclass objects can be created. The subclasses must implement the pure virtual function.
class SavingAccount : public BankAccount {
public:
SavingAccount(int acctnum) :
BankAccount(acctnum)
{}
BankAccount & deposit(float amount)
{balance += amount;}
void printOn(ostream & o) {o <<
"SavingAcct #" << accountNumber << "\n";}
};
void main() {
// BankAccount
b(1000);
//ERROR: NOT LEGAL BankAccount is an abstract class
SavingAccount s(1001);
s.deposit(500.0);
cout << s;
}
SavingAccount #1001
Combining Static and Dynamic Binding
Because static binding is faster than dynamic binding, C++ code sometimes combines the two. The dynamically bound methods are used to define the public protocol of classes, but statically bound methods are used to do private work. This allows both extendable decoupling and avoids overhead in private work.
/*
This example shows static and dynamic binding
being combined. The BankAccount class defines
a public, and virtual, protocol for doing deposits and withdrawls. The
subclasses are expected
to implement this protocol and apply their service charges. The subclasses will
then use the
statically bound basicDeposit and basicWithdraw methods of the BankAccount class
to make the
actual changes to the account balance.
*/
class
BankAccount{BankAccount(int
acctnum) : accountNumber(acctnum), balance(0) {}
//dynamically bound public protocol
virtual BankAccount & deposit(double
anAmount)=0;
virtual BankAccount & withdraw(double
anAmount)=0;
virtual void
printOn(ostream & o) {o << "Account #" <<
accountNumber << " $" << balance;}
//statically bound protected methods
BankAccount & basicWithdraw(double
amount) {
balance -= amount;
return *this;
}
};
ostream & operator<<(ostream
& o, BankAccount & b) {
b.printOn(o); return o;
}
class
SavingAccount : public BankAccount {BankAccount & withdraw(double
anAmount){
basicWithdraw(anAmount);
basicWithdraw(anAmount * serviceChargeRate);
return *this;
}
void printOn(ostream
& o) {
o << "Saving ";
BankAccount::printOn(o);
o << "\n";
}
};
double
SavingAccount::serviceChargeRate = 0.05;#include
<iostream>
using namespace
std;
#include "bankaccount.h"
void main() {
SavingAccount s1(1000);
SavingAccount s2(1001);
s1.deposit(1000.00).withdraw(300);
cout << s1 << s2;
}
public inheritance is approriate for an "Is a" relationship. A subclass object behaves like a superclass object, because it is a superclass object as well. For example, a SavingAccount is a BankAccount, so if you can deposit into a BankAccount, you should be able to deposit into a SavingAccount.
#include
<iostream>using
namespace std;class
BankAccount{ostream &
operator<<(ostream
& o, BankAccount & b) {
b.printOn(o); return
o;
}
class
SavingAccount : public BankAccount {void
main() {SavingAccount #1001
$500.0
Destruction -Why Destructors need to be virtual
#include
<iostream>
using namespace std;
class A{
public:
A() {cout << "A()\n";}
~A() {cout <<
"~A()\n";}
//should be virtual
};
class B :
public A {
public:
B() {cout << "B()\n";}
~B() {cout << "~B()\n";}
};
void main() {
A * ap;
ap = new B;
delete ap;
}
/*output
A()
B()
~A() ---destroyed as an A instance not
B instance
---solution: make the
destructors virtual.
*/
Private inheritance is approriate for an "Part of" relationship. A subclass object does not publicly behave like a superclass object, because "is not" superclass object as well. Instead the superclass object is helping to add private behaviour. For example, a Stack might use Vector code, but a Stack should not behave like a Vector. Do not confuse private inheritance with true aggregation. With true aggregation a Stack would actually contain a Vector object.
};
class Stack {
Vector v; //Stack
contains a Vector Object
int top;
public:
Stack(void) : top(0) {}
Stack & push(T item)
{v[top++]
= item; return *this;}
T pop(){return v[--top] ;}
};
void main() {
Stack s;
s.push(5).push(7);
cout << s.pop();
//cout << s[1]; NOT LEGAL
}
typedef int T;
class Vector{
T * buffer;
public:
Vector(void) : buffer(new T[100]) {}
~Vector(void) {delete [] buffer;}
T& operator[](int index) {return
buffer[index];
}
};
class Stack : private
Vector {
protected:
int top;
public:
Stack(void) : top(0) {}
Stack & push(T item)
{operator[](top++)
= item; return *this;}
T pop(){return operator[](--top);}
};
/*
//NOT LEGAL with private
inheritance
//BoundedStack cannot
"see"
operator[]
class BoundedStack :
public
Stack{
int bound;
public:
BoundedStack(int
b) : bound(b) {}
Stack & push(T
item) {
if(top <
bound) operator[](top++)
= item; return *this;}
};
*/
void main() {
Stack s;
s.push(5).push(7);
cout << s.pop();
//cout << s[1]; NOT LEGAL, the operator[]
is private to Stack
}
typedef int T;
class Vector{
T * buffer;
public:
Vector(void) : buffer(new T[100]) {}
~Vector(void) {delete [] buffer;}
T& operator[](int index) {return
buffer[index];
}
};
class Stack : protected
Vector {
protected:
int top;
public:
Stack(void) : top(0) {}
Stack & push(T item)
{operator[](top++)
= item; return *this;}
T pop(){return operator[](--top);}
};
//LEGAL with protected
inheritance
//BoundedStack can "see"
operator[]
class BoundedStack :
public
Stack{
int bound;
public:
BoundedStack(int
b) : bound(b) {}
Stack & push(T
item) {
if(top <
bound) operator[](top++)
= item; return *this;}
};
void main() {
BoundedStack s(50);
s.push(5).push(7);
cout << s.pop();
//cout << s[1]; NOT LEGAL, the operator[]
is private to Stack
}
The following slides illustrate how virtual
functions
are implemented in C++


