C++继承与多态
继承
继承和派生的含义
如果要提高程序开发的效率,需要对已有的代码进行重用。在面向过程的语言中,重用的单位是函数(过程)。而在面向对象的语言中,重用的单位是类。
重用一个已有的类来创建新的类,一种方法是组合,它体现部分和整体的关系。
比如说要构造一辆汽车,汽车是一种复杂的对象,它由很多小的部分组成。

再举一个更具体简单的例子:构造一个平面图形系统,需要用到点、线段、圆、矩形、三角形等。在线段、圆、矩形、三角形等图形中,都需要表示点这个类型,比如线段需要有两个端点来表示,我们可以通过添加点类型的成员,把它们组合起来。

或者可以把它们之间的关系更明确的表示为:

再比如说一个学生类。

继承(派生)是类的重用的另一种方法,它体现类之间的is a的关系(一般和特殊的关系)。如果A is a B,即A是某一种特殊的B,则我们说A类是从B类继承(inherit)的,或者说B派生(derive)出A。比如说交通工具:

如果A类从B类继承,或B派生出A,则我们称A类是B类的派生类(Derived Class),B类是A类的基类(Base Class);或者说A类是B类的子类(Child Class),B类是A类的父类(Parent Class)。
- 逻辑上派生类和基类是一种is a的关系;
- 派生类是在基类的基础上添加新的成员进行扩充而得到,它拥有基类的所有成员(所有属性和方法);
- 在用到基类对象的地方,可以用派生类对象来代替。
比如说,学生Student是一类学校的成员SchoolMember,老师Teacher也是一类学校的成员SchoolMember,研究生GraduateStudent是一类学生Student。

还有各种图形之间的关系:

不恰当使用继承的例子:
- Rectangle继承Point?

- Ellipse继承Circle还是Circle继承Ellipse?

派生类的定义
我们用C++语言来描述学生老师之间的关系:
struct Date {
int year;
int month;
int day;
};
struct Course {
string name;
int credit;
};
class SchoolMember {
private:
string name;
Date birth;
bool gender;
};
//Student类继承了SchoolMember类,public指明继承方式是公有继承
class Student : public SchoolMember
{
private:
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
};
class Teacher; //前置声明
//GraduateStudent类继承了Student类
class GraduateStudent : public Student {
private:
Teacher *tutor;
};
//Teacher类继承了SchoolMember类
class Teacher : public SchoolMember
{
private:
int title;
int salary;
GraduateStudent *supervise[8];
int num_supervise;
};派生类对象的定义:
int main() {
SchoolMember p;
Student s;
Teacher t;
GraduateStudent g;
}对象的内存布局:

加上成员函数的实现:
struct Date {
int year;
int month;
int day;
};
struct Course {
string name;
int credit;
};
class SchoolMember {
private:
string name;
Date birth;
bool gender;
public:
Date get_bith() {
return birth;
}
string get_name() {
return name;
}
bool get_gender() {
return gender;
}
};
//Student类继承了SchoolMember类,public指明继承方式是公有继承
class Student : public SchoolMember
{
private:
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
public:
string get_id() {
return id;
}
int get_department() {
return department;
}
int get_credit() {
return credit();
}
Date get_enroll_day() {
return enroll_day;
}
Date get_graduate_day() {
return graduate_day;
}
void enroll(Date day) {
enroll_day = day;
}
void graduate(Date day) {
graduate_day = day;
}
};
//GraduateStudent类继承了Student类
class GraduateStudent : public Student {
private:
Teacher *tutor;
public:
Teacher *get_tutor() {
return tutor;
}
void set_tutor(Teacher *t) {
if(tutor == t) return;
tutor = t;
t->add_supervise(this);
}
};
//Teacher类继承了SchoolMember类
class Teacher : public SchoolMember
{
private:
int title;
int salary;
GraduateStudent *supervise[8];
int num_supervise;
public:
void add_supervise(GraduateStudent *g) {
for(int i = 0; i < num_supervise; i++)
if(supervise[i] == g)
return;
supervise[num_supervise++] = g;
g->set_tutor(this);
}
};练习:将各种图形类用C++描述出来。
派生类的构造和析构
在创建对象时,我们需要对对象进行初始化。普通类型的对象的初始化是由构造函数完成的,对于派生类对象也是相同的。
创建派生类对象时,派生类的构造函数会被自动调用。
struct Date {
int year, month, day;
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
};
class SchoolMember {
private:
string name;
Date birth
bool gender;
};
class Student : public SchoolMember {
private:
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
public:
Student(string n, Date b, bool g, string i, int d)
: enroll_day(0,0,0), graduate_day(0,0,0) {
id = i;
department = d;
credit = 0;
}
};
int main() {
Student d("Jack", Date(1985, 9, 13), true, "05011101", 11);
}这个例子里面,派生类Student扩展的部分已经初始化,但是基类的部分没有初始化。要初始化继承下来的基类部分的成员,可以在基类中定义构造函数。
struct Date {
int year, month, day;
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
};
class SchoolMember {
private:
string name;
Date birth
bool gender;
public:
SchoolMember(string n, Date b, bool g) : birth(b) {
name = n;
gender = g;
}
};
class Student : public SchoolMember {
private:
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
public:
Student(string n, Date b, bool g, string i, int d)
: enroll_day(0,0,0), graduate_day(0,0,0), SchoolMember(n, b, g) {
id = i;
department = d;
credit = 0;
}
};
int main() {
Student d("Jack", Date(1985, 9, 13), true, "05011101", 11);
}如果基类中有构造函数,派生类没有定义构造函数,或者有构造函数但是没有显示调用基类构造函数,那么基类中的默认构造函数会被自动调用。
class SchoolMember {
string name;
Date birth;
bool gender;
public:
SchoolMember(string n = "", Date b = Date(0,0,0), bool g = false)
: birth(b) {
name = n;
gender = g;
}
};
class Student : public SchoolMember{
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
};
int main() {
Student s;
}在一个多层的继承结构里面,创建一个派生类对象时,基类、基类的基类、基类的基类的基类等的构造函数都会被调用。
class SchoolMember {
private:
string name;
Date birth
bool gender;
public:
SchoolMember(string n, Date b, bool g) : birth(b) {
name = n;
gender = g;
}
};
class Student : public SchoolMember {
private:
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
public:
Student(string n, Date b, bool g, string i, int d)
: enroll_day(0,0,0), graduate_day(0,0,0), SchoolMember(n, b, g) {
id = i;
department = d;
credit = 0;
}
};
class GraduateStudent: public Student {
Teacher *tutor;
public:
GraduateStudent(string n, Date b, bool g, string i, int d, Teacher *t)
:Student(n, b, g, i, d) {
tutor = t;
}
};
int main() {
Teacher teacher;
GraduateStudent d("Rose", Date(1985, 9, 13), false, "05011101", 11, &teacher);
}类的完整实现实例:
class Date {
public:
int year, month, day;
Date(int y, int m, int d)
: year(y), month(m), day(d) {
}
};
struct Course{
string name;
int credit;
};
class SchoolMember {
string name;
Date birth;
bool gender;
public:
SchoolMember(string n, Date b, bool g)
: name(n), birth(b), gender(g) {
}
};
class GraduateStudent;
class Teacher : public SchoolMember {
int salary;
int title;
int num_supervise;
GraduateStudent *supervise[8];
public:
Teacher(string n, Date b, bool g, int t, int s)
: SchoolMember(n, b, g), salary(s), title(t) {
}
void add_supervise(GraduateStudent *g) {
for(int i = 0; i < num_supervise; i++)
if(supervise[i] == g)
return;
supervise[num_supervise++] = g;
g->set_tutor(this);
}
};
class Student: public SchoolMember {
string id;
int department;
int credit;
Date enroll_day;
Date graduate_day;
public:
SchoolMember(string n, Date b, bool g, string i, int d)
: SchoolMember(n, b, g), id(i), department(d), credit(0),
enroll_day(0,0,0), graduate_day(0,0,0) {
}
void take_course(Course *c) {
credit += c->credit;
}
void enroll(Date day) {
enroll_day = day;
}
void graduate(Date day) {
graduate_day = day;
}
};
class GraduateStudent: public Student {
Teacher *tutor;
public:
GraduateStudent(string n, Date b, bool g, string i, int d, Teacher *t)
: Student(n, b, g, i, d), tutor(t) {
t->add_supervise(this);
}
void set_tutor(Teacher *t) {
if(tutor == t) return;
tutor = t;
tutor->add_supervise(this);
}
};派生类对象被销毁时,基类的析构函数会被自动调用。如果派生类和基类都有析构函数,那么他们都会被调用。
class SchoolMember {
public:
SchoolMember() {
cout << "construct SchoolMember" << endl;
}
~SchoolMember() {
cout <<"destruct SchoolMember" <<endl;
}
};
class Student : public SchoolMember{
public:
Student() {
cout << "construct Student" << endl;
}
~Student() {
cout << "destruct Student" << endl;
}
};
int main() {
Student s;
SchoolMember p;
}派生类析构函数只负责派生类增加部分的分配资源的析构。
int MAX_SUPERVISE[] = {0, 0, 4, 10};
class Teacher : public SchoolMember {
int salary;
int title;
int num_supervise;
int capacity_supervise;
GraduateStudent **supervise;
public:
Teacher(string n, Date b, bool g, int t, int s)
: SchoolMember(n, b, g), salary(s), title(t) {
}
Teacher(string n, Date b, bool g, int t, int s)
:SchoolMember(n,b,g), salary(s), title(t) {
capacity_supervise = MAX_SUPERVISE[t];
supervise = new GraduateStudent*[capacity_supervise]
}
~Teacher(){
delete[] supervise;
}
void add_supervise(GraduateStudent *g) {
for(int i = 0; i < num_supervise; i++)
if(supervise[i] == g)
return;
if(num_supervise >= capacity_supervise)
return;
supervise[num_supervise++] = g;
g->set_tutor(this);
}
};
int main() {
Teacher t("czk", Date(1979,8,27), true, 1, 1500);
}类型兼容性
在需要使用基类对象的情况下,可以用派生类对象来代替。分为三种情况:
- 派生类对象取代基类对象,派生类对象被切割
- 派生类对象指针取代基类对象指针
- 派生类对象引用取代基类对象引用
int main() {
Student s("jack", Date(1980, 8, 8), true, "970101", 10);
SchoolMember p = s; //slice
Student s2 = p; //error
Student *pD = new Student("jack", Date(1980, 8, 8), true, "970101", 10);
SchoolMember *pB = pD; //correct 基类指针指向派生类对象
SchoolMember *pB2 = new Student("jack", Date(1980, 8, 8), true, "970101", 10);
pB = new Teacher("jack", Date(1980, 8, 8), true, 4, 1500); //多态性在这里体现
Student &rd = s;
SchoolMember &rp = p;
Student &rd2 = p; //error
SchoolMember &rp2 = s; // ok
}指针类型的强制转换
int main() {
SchoolMember *pB = new Student;
Student *pD2 = pB; //error
Student *pD3 = dynamic_cast<Student *>(pB);
SchoolMember *pB = new Teacher;
Student *pD = dynamic_cast<Student *>(pB); //转换失败,得到空指针
Student *pD2 = static_cast<Student *>(pB); //static_cast转换时不做检查,访问pD2的后果不堪设想
pD2->enroll(); //不可设想的后果
}派生类的访问权限
- private成员:只在定义它的类的成员函数中可以访问,在派生类的成员函数中不可访问。基类的私有成员被派生类继承,但是不能被派生类访问
- protected成员:在本类及派生类中能访问的成员。派生类可以访问继承的保护成员,但是不能访问一个基类对象的保护成员
class Base {
public:
int a;
protected:
int b;
private:
int c;
};
class Derived :public Base {
void f() {
cout << a << b << c;//访问c是错误的
}
};
void f(Derived &d) {
cout << d.a<< d.b<< d.c; //访问b,c都是错的
}class Base {
protected:
int x;
};
class Derived : public Base{
public:
void f( ) {
Base b;
cout << b.x; //访问基类对象的保护成员,错误
cout << x; //发访问继承的基类保护成员,正确
}
};使用using声明改变基类成员权限
class Person {
public:
string get_name();
Date get_date();
bool get_gender();
};
class Student : public Person {
private:
using Person::get_name;
};派生类的成员名字如果与基类相同,将隐藏基类成员
class Person {
public:
void set(string name);
void set(bool gender);
};
class Student : public Person{
public:
void set(int department); // hide base member functions
};
void f(Student &s) {
s.set("12345");
s.set( true );
s.Person::set("12345"); //ok
s.Person::set(true); //ok
}保护继承和私有继承
class Derived : private Base {
//…
};
class Derived: protected Base {
//…
};class Base {
public: int a;
protected: int b;
private: int c;
};
class Derived : private Base{
void f() {
cout << a << b << c;
}
};
int main() {
Derived d;
cout << d.a << d.b << d.c;
}多继承
在C++中允许一个派生类有多个基类,这种继承叫做多继承。相对的只有一个基类的继承被称为单继承。多继承表达的是“派生类既是一种基类A,又是一种基类B”的逻辑关系。
比如Assistent类继承了Student和Teacher两个类:
class Assistant : public Student, public Teacher {
public:
Assistant(string n, string i, string l)
: Teacher(n, l), Student(n, i) {
cout << "a";
}
void print() const {
cout<< name << id << level; // ambiguous error
cout<< Student::name << Teacher::name << id << level;
}
};注意:不同基类中的同名的成员会在派生类中同时存在。比如这里的name,id,level等。
多继承时对象的复制:派生类对象的指针可以赋给任何一个基类类型的指针
Assistant assist("jack", "12456", "assistant");
Student s = assist; //OK, slice
Teacher t = assist; //OK, slice
Student *ps = &assist; //OK
Teacher *pt= &assist; //OK
ps->print();
pt->print();
Person *p = &assist; //ambigius
Person *p = (Student *)&assist;
Person *p = (Teacher *)&assist;
p->print();虚基类
class Person {
};
class Student : virtual public Person {
};
class Teacher : virtual public Person {
};
class Assistant : public Student, public Teacher {
};虚基类Person在派生类Assistant的对象中只有一份
class Person {
string name;
public:
Person(string n):name(n){ cout << "P"; }
};
class Student : virtual public Person {
string id;
public:
Student(string n, string i):id(i), Person(n){ cout<<"S";}
};
class Teacher : virtual public Person {
string level;
public:
Teacher(string n, string l):level(l), Person(n){cout<<"T";}
};
class Assistant : public Student, public Teacher {
public:
Assistant(string n, string i, string l)
: Student(n, i), Teacher(n, l), Person(n) {cout<<"A";}
};完整例子:
#include <iostream>
#include <string>
using namespace std;
struct Date {
int year;
int month;
int day;
public:
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
void print() {
cout << year<<"-" <<month<<"-" <<day;
}
};
const bool MALE = false;
const bool FEMALE = true;
enum {SCHOOLMEMBER, TEACHER, STUDENT, GRADUATESTUDENT};
class SchoolMember {
string name;
Date birth;
bool gender;
public:
int type;
SchoolMember(string n, Date b, bool g)
: birth(b) {
name = n;
gender = g;
type = SCHOOLMEMBER;
}
string get_name() {
return name;
}
Date get_birth() {
return birth;
}
bool get_gender() {
return gender;
}
virtual void print() {
cout << name;
birth.print();
cout << (gender==MALE?"MALE":"FEMALE") <<endl;
}
virtual ~SchoolMember() {
cout << "destruct SchoolMember " << get_name() << endl;
}
};
struct Course {
string name;
int credit;
};
enum { MATHS = 8, PHYSICS =9, CS = 10};
class Student : virtual public SchoolMember {
string id;
int department;
int credit;
public:
Student(string n, Date b, bool g, string i, int d)
: SchoolMember(n,b,g)
{
id = i;
department = d;
credit = 0;
type = STUDENT;
}
void take_course(Course *c){
credit += c->credit;
}
bool can_graduate() {
return credit >= 160;
}
string get_id() {
return id;
}
int get_credit() {
return credit;
}
int get_department() {
return department;
}
virtual void print() {
cout << get_name();
get_birth().print();
cout << (get_gender()==MALE?"MALE":"FEMALE") << id << credit <<department << endl;
}
~Student() {
cout << "destruct Student:" << get_name() << endl;
}
};
class Graduate;
enum {PROFESSOR =4, VICEPROFESSOR = 3, ASSISTANT = 1};
class Teacher :virtual public SchoolMember {
int title;
int salary;
Graduate* supervised[10];
int num_supervised;
public:
Teacher(string n, Date b, bool g, int t, int s)
: SchoolMember(n, b, g) {
title = t;
salary = s;
num_supervised = 0;
type = TEACHER;
}
void add_supervised(Graduate *g) {
supervised[num_supervised] = g;
num_supervised++;
}
virtual void print() {
cout << get_name();
get_birth().print();
cout << (get_gender()==MALE?"MALE":"FEMALE") << title << salary << endl;
}
~Teacher() {
cout << "destruct Teacher:" << get_name() << endl;
}
};
class Graduate :public Student {
Teacher *tutor;
public:
Graduate(string n, Date b, bool g, string i, int d, Teacher *t)
: Student(n, b, g, i, d), SchoolMember(n, b,g)
{
tutor = t;
type = GRADUATESTUDENT;
}
void set_tutor(Teacher *t){
tutor = t;
}
virtual void print() {
cout << get_name();
get_birth().print();
cout << (get_gender()==MALE?"MALE":"FEMALE") << get_id() << get_credit()<< get_department() << tutor->get_name() << endl;
}
~Graduate() {
cout << "destruct Graduate:" << get_name() << endl;
}
};
class Assistant : public Student, public Teacher {
public:
Assistant(string n, Date b, bool g, string i, int d, int t, int s)
:Student(n, b, g, i, d), Teacher(n, b, g, t, s),
SchoolMember(n, b, g) {
}
void print() {
}
};
void printall(SchoolMember *p[], int n) {
for(int i = 0; i < n; i++) {
p[i]->print();
}
}
int main() {
SchoolMember *members[100];
members[0] = new SchoolMember("Jack", Date(1980, 1, 1), MALE);
members[1] = new Student("Mike", Date(1985, 5, 5), MALE, "12345", CS);
members[2] = new Teacher("Rose", Date(1970, 3, 3), FEMALE, PROFESSOR, 2000);
members[3] = new Graduate("Tom", Date(1982, 2, 2), MALE, "54321",
CS, dynamic_cast<Teacher*>(members[2]));
printall(members, 4);
for(int i = 0;i < 4; i++)
delete members[i];
Assistant a("Jeff", Date(1978, 5, 5), MALE, "11100", CS, ASSISTANT, 800);
SchoolMember *s = &a;
}多态
多态与虚函数
相同的指令,作用在不同类型的对象上,产生不同动作。 比如:很多动物,有猫、狗、老虎等等。现在让所有的动物做一个“叫”的动作,结果…… 比如:有很多图形,有直线、方形、圆形、椭圆等等。现在让所有的图形把自己画在屏幕上,结果……
int main() {
SchoolMember *p[100];
p[0] = new Student(“Jack”, Date(1984, 1,1), false, “20111374”, 11);
p[1] = new Teacher(“Marry”, Date(1969, 5,5), true, “lecturer”, 1000);
p[2] = new Student(“David”, Date(1983, 11,11), false, “20112343”, 11);
//...
}现在要求写一个函数printall显示所有人的详细信息,应该怎么做?
class SchoolMember {
enum MemberType{ P, S, T, G } type; //用来区分学生还是教师
};
void printall( SchoolMember *p[100] ) {
for( int i = 0; i < 100; i++) {
cout << p[i]->name << p[i]->birth << p[i]->gender;
switch(p[i]->type) {
case S: {
Student *s = (Student*)(p[i]);
cout << s->id << s->department;
} break;
case T: {
Teacher *t = (Teacher*)(p[i]);
cout << t->level << t->salary;
} break;
}
}
}在每个类上增加一个print函数:
class SchoolMember {
public:
void print(){ cout << name << birth <<gender; }
};
class Student : public SchoolMember{
public:
void print() {
cout << name << birth << gender << ID << department;
}
};
class Teacher : public Person {
public:
void print() {
cout << name << birth << gender << level << salary;
}
};
void printall( Person* p[100] ) {
for(int i = 0; i < 100; i++)
switch (p[i]->type) {
case S:
{
Student *s = (Student*)(p[i]);
s->print();
} break;
case T:
{
Teacher *t = (Teacher*)(p[i]);
t->print();
} break;
}
}仍不够简洁,理想的做法是:
void printall(Person *p[100]) {
for ( int i =0; i < 100; i++)
p[i]->print( );
}但是,这时输出是
Jack 1984-1-1 false
Marry 1969-5-5 true
David 1983-3-3 false
……
虚函数
class Person {
protected:
string name;
Date birth;
bool gender;
public:
virtual void print( ) { //虚函数
cout << name << birth << gender;
}
};在派生类中覆盖(override)虚函数
class Student : public Person {
protected:
string ID;
int department;
public:
virtual void print( ) { // override Base::print
cout << name << birth <<gender;
cout << ID << department;
}
};
class Teacher : public Person{
protected:
int level;
double salary;
public:
virtual void print() { //override Base::print
cout << name << birth << gender;
cout << level << salary;
}
};
void printall(Person *p[100]) {
for ( int i =0; i < 100; i++)
p[i]->print( );
}这时候的结果是:
Jack 1984-1-1 false 20111374 11
Marry 1969-5-5 true lecturer 1000
David 1983-11-11 false 2012343 11
……
调用被覆盖(override)的函数
class Teacher : public Person {
protected:
string level;
double salary;
public:
virtual void print() {
Person::print();
cout << level << salary;
}
};多态的限制
int main() {
Person p(“Jack”, Date(1984,1,1), false);
p.print(); // 这里调用Person::print
Student s(“David”, Date(1983,11,11), false, 403315,10);
s.print(); // 这里调用 Student::print
Person p2 = s;
p2.print(); // 这里调用 Person::print
Person *p3 = &s;
p3->print(); // 这里调用 Student::print 这里才是多态
Person &p4 = s;
p4.print();
}多态只能通过指针或者引用来实现。
非成员函数、类的构造函数、静态成员函数不能是虚函数
virtual写在成员函数的声明中,而不是定义中
一般情况下,派生类要override基类的虚函数,要求函数声明形式完全一样。
例外:基类的虚函数返回基类的指针或者引用,那么派生类override它,可以返回此派生类的指针或引用
class Base { virtual Base*f(); }; class Derived:public Base { virtual Derived* f(); };如果派生类没有override基类的虚函数,那么派生类会继承基类的虚函数
更多例子:
class Shape {
public:
virtual void draw();
};
class Rectangle : public Shape{
public:
virtual void draw();
};
class Circle :public Shape {
public:
virtual void draw();
};
int main() {
Rectangle r;
Circle c;
Shape &s = r;
s.draw();
Shape &t = c;
t.draw();
}问题:
class Shape {
public:
//no virtual function in base
};
class Rectangle : public Shape{
public:
virtual void draw() { /*....*/ }
};
class Circle :public Shape {
public:
virtual void draw() { /*....*/ }
};
void f(Shape* s) {
s->draw();
}
int main() {
Shape *s = new Rectangle;
f( s );
}基类中没有虚函数draw,则s->draw()出错。
问题:
class Shape {
public:
virtual void draw() {/*....*/}
};
class Rectangle : public Shape{
public:
virtual void draw(int a){/*....*/}
};
class Circle :public Shape {
public:
virtual void draw() {/*....*/}
};
void f(Shape* s) {
s->draw();
}
int main() {
Shape *s = new Rectangle;
f( s );
Shape *c = new Circle;
f( c );
}Rectangle中定义的draw与基类中的虚函数draw不同,则基类中的draw没有被override。
一个函数调用如果在编译时候确定调用哪个函数,我们称它为静态绑定;如果编译时候不能确定,到运行时才能确定,那么我们称它为动态绑定。
void f(Person *p) {
p->print( ); //static or dynamic?
}一般虚函数使用动态绑定,普通函数使用静态绑定。动态绑定比静态绑定速度慢。
虚表(virtual table)——C++实现多态的方法 
虚析构函数
int main() {
Person *p = new Student(...);
delete p; // which destructor is called?
}虚析构函数作用:通过基类指针来释放派生类对象时,能够保证调用正确的析构函数
class Person {
public:
virtual ~Person() { };
};更多例子:动物的叫的例子
class Animal {
public:
virtual void shout() { cout << "animal cannot shout"; }
};
class Dog : public Animal {
public:
void shout() { cout << "Dog barking!"; }
};
class Ox : public Animal {
public:
void shout() { cout << "Ox Moo"; }
};
class Fish: public Animal {
//shout not overridden here, Animal::shout is inherited
};
void f(Animal *a) {
a->shout();
}
int main () {
int i; cin >> i;
Animal *a;
switch(i) {
case 1: a = new Dog; break;
case 2: a = new Ox; break;
case 3: a = new Fish; break;
}
f(a);
delete a;
}纯虚函数和抽象类
问题:某些基类的虚函数没有实现的意义
class Animal {
public:
virtual void shout() { cout << "animal cannot shout"; }
};纯虚函数(pure virtual function):函数体可以省略的虚函数
class Animal {
public:
virtual void shout() = 0; //Pure virtual function
};抽象类(abstract class):含有纯虚函数的类称为抽象类。类中只要有一个函数是纯虚函数,这个类就是抽象类。抽象类不能实例化,即不能定义对象。
void f() {
Animal a; // error
Animal *pa1 = new Animal; // error
Animal *pa2 = new Ox;
pa2->shout();
Animal *pa3 = new Fish;
pa3->shout();
}与抽象类相对的是实体类(concrete class),即不包含纯虚函数的类
抽象类虽然不能够实例化,但可以派生。在派生类中,纯虚函数会被继承。如果在派生类中,给出了所有纯虚函数的实现,那么派生类将成为实体类
class Shape { //abstract class
public:
virtual void draw() =0;
virtual double getarea() = 0;
};
class Rectangle :public Shape {
public:
virtual void draw() {/*...*/}
virtual double getarea() { /*...*/}
};
class Circle : public Shape{
public:
virtual void draw() { /*...*/}
};Rectangle为实体类,Circle为抽象类
纯虚函数一般省略函数体,但也可以有函数体
class Person {
public:
virtual void print() = 0 {
cout << name << gender << birth;
}
};带函数体的纯虚函数可以在派生类中被调用
class Student : public Person{
public:
virtual void print() {
Person::print(); //base pure virtual function is called
cout << id << department;
}
};接口类:只有纯虚函数成员的类。 IUnknown:微软所有COM(Component Object Model)对象的基类
class IUnknown {
public:
virtual long QueryInterface(
REFIID riid, void **ppvObject) = 0;
virtual unsigned long AddRef( void) = 0;
virtual unsigned long Release( void) = 0;
};运行时类型信息
问题:某些情况下,光靠多态性不够用,必须得到对象准确的类型信息
void f(Shape *s) {
// what on earth is s?
}两种RTTI机制:dynamic_cast和typeid。先决条件:类中必须有虚函数。在使用RTTI之前,需要先审查在面向对象设计上是否出了问题。
dynamic_cast
void f(Person *p) {
if( dynamic_cast<Student *>(p) ) {
cout<<"p is a Student object or its derivation";
Student *s = dynamic_cast<Student *>(p);
} else if( dynamic_cast<Teacher *>(p) ) {
cout<<"p is a Teacher object or its derivation";
Teacher * t = dynamic_cast<Teacher *>(p);
}
}typeid能够确定表达式的确切类型,使用时要包含<typeinfo>
void f( Shape *s) {
cout << typeid(double).name();
cout << typeid(5280L).name();
cout << typeid(s).name(); //runtime class name
cout << typeid(*s).name(); //static or dynamic?
Shape &r = *s;
cout << typeid(r).name();
Shape a;
cout << typeid(a).name();
}typeid返回一个typeinfo类对象的引用
class type_info{
public:
virtual ~type_info();
const char *name() const;
bool operator==(const type_info&) const;
bool operator!=(const type_info&) const;
bool before(const type_info&) const;
private:
type_info( const type_info&);
type_info& operator=(const type_info&);
};用法:
void f(Shape *s) {
if( typeid( *s ) == typeid( Circle ) )
cout << "s is a Circle";
}面向对象设计方法
- 确定程序中需要的类及每个类需要的操作
- 将不同类的共同的操作提取到基类
- 通过基类接口来操纵各种不同的对象
- 可以通过增加新的派生类来扩展系统
例子:
复合文档(composite模式):包含文字、图形的文档

figure11.png 一组数据,有多种表现形式(observer模式)(比如表格、柱状图、柄图)
交互图 
设计模式:
前人成功经验总结的可以重复利用的面向对象软件设计的范例。用处:套用前人范式以面向对象思想解决问题,设计出健壮、可复用、支持变化的软件。参考书:《设计模式——可复用面向对象软件的基础》——GoF(Erich Gamma等四人)