public class Holiday extends Date {
class Holiday : public Date {
we will always use public inheritance.
class Child : public Parent1, public Parent2 {
class Date {
int day, month, year;
public:
Date(); // today's date
Date(int d, int m);
Date(int d, int m, int y);
int get_day() const { return day; }
int get_month() const { return month; }
int get_year() const { return year; }
};
The members of base class(es) are initialized similarly to subobjects:
class Holiday : public Date {
string name;
public:
Holiday(string n) : Date(), name(n) {}
Holiday(string n, int d, int m) :
Date(d, m), name(n) {}
string get_name() const { return name; }
};
Members of the base class can't be initialized directly:
use Date.
Initialization is done in the following order:
Holiday holiday("Anzac Day", 25, 4);
Date d = holiday;
initializes d as a copy of the Date part of holiday.
d = holiday;
copies the Date part of holiday into d.
final int non_redefinable_method() { ... }
int redefinable_method() { ... }
abstract int undefined_method();
int non_redefinable_method() { ... }
virtual int redefinable_method() { ... }
virtual int undefined_method() = 0;
The latter is called a pure virtual function.
When a method is declared virtual in a base class, it is also virtual in derived classes (the keyword is optional).
Overridable methods must be declared virtual:
class Date {
...
virtual string desc() const { ... }
};
overriding in a derived class:
class Holiday : public Date {
...
virtual string desc() const {
return name + " " + Date::desc();
}
};
Note: qualify with the class name to get the base version.
void print_day1(Date d) {
cout << "It's " << d.desc() << '\n';
}
void print_day2(Date &d) {
cout << "It's " << d.desc() << '\n';
}
then
Holiday xmas("Christmas", 25, 12);
print_day1(xmas); // It's 25/12/2004
print_day2(xmas); // It's Christmas 25/12/2004
class Pet {
protected:
string _name;
public:
Pet(string name) : _name(name) {}
virtual string sound() const = 0;
virtual void speak() const {
cout << _name << ": " << sound() << "!\n";
}
};
As in Java, abstract classes may not be instantiated,
so no variable may have type Pet,
but we can declare a reference.
class Dog : public Pet {
public:
Dog(string name) : Pet(name) {}
string sound() const { return "woof"; }
void speak() const { // virtual is optional
Pet::speak();
cout << '(' << _name << " wags tail)\n";
}
};
class Cat : public Pet {
public:
Cat(string name) : Pet(name) {}
virtual string sound() const { return "miao"; }
};
void speakTwice(const Pet &pet) {
pet.speak();
pet.speak();
}
Then we can write
Dog a_dog("Fido", 30);
speakTwice(a_dog);
Cat a_cat("Tiddles");
speakTwice(a_cat);
class A {
virtual f(int n, Point p) { ... }
}
Now suppose we intend to override f in a derived class,
but make a mistake with the argument types:
class B : public A {
f(Point p, int n) { ... }
}
This will be accepted as a definition of a new and different member function.
class Pet {};
class Cat : public Pet {};
void wash(Pet &x) { ... }
void wash(Cat &x) { ... }
int main() {
Cat felix;
wash(felix); // both functions match; second is used
}
class Pet {};
class Dog : public Pet {};
class Cat : public Pet {};
void chase(Pet &x, Cat &y) { ... }
void chase(Dog &x, Pet &y) { ... }
int main() {
Dog buster;
Cat tom;
chase(buster, tom); // ambiguous!
}
Cat felix;
Pet *p = &felix;
Because the pointer is copied (not the object) no slicing occurs:
p->speak(); // miao
The speak method uses the virtual method sound,
which is defined in the Cat class,
and selected by dynamic binding.
Often a container holds pointers to a base type:
vector<Pet *> pets;
Cat felix("Felix");
Dog fido("Fido");
pets.push_back(&felix);
pets.push_back(&fido);
When we access elements of the vector,
dynamic binding is used:
for (int i = 0; i < pets.size(); i++)
pet[i]->speak(); // miao, woof
Cat *cp = new Cat("tiddles");
pets.push_back(cp);
Here the pointer cp is local,
but the object it points at is on the heap
(so it outlasts the current block).
If Cat is a subtype of Pet,
vector<Cat *> cats;
vector<Pet *> *p = &cats; // illegal
Dog fido;
p->push_back(&fido); // would be trouble
template <typename T>
class History { ... };
template <typename T>
class MyHistory : public History<T> { ... };
class BrowserHistory : History<string> { ... };
template <typename T>
class PointerHistory : History<T *> { ... };