TableOfContents

继承

1. 继承和派生的含义

如果要提高程序开发的效率,需要对已有的代码进行重用。在面向过程的语言中,重用的单位是函数(过程)。而在面向对象的语言中,重用的单位是类。

重用一个已有的类来创建新的类,一种方法是组合,它体现部分和整体的关系。

比如说要构造一辆汽车,汽车是一种复杂的对象,它由很多小的部分组成。

attachment:figure4.png

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

attachment:figure2.png

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

attachment:figure1.png

再比如说一个学生类。

attachment:figure3.png

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

attachment:figure5.png

如果A类从B类继承,或B派生出A,则我们称A类是B类的派生类(Derived Class),B类是A类的基类(Base Class);或者说A类是B类的子类(Child Class),B类是A类的父类(Parent Class)。

比如说,学生Student是一类学校的成员SchoolMember,老师Teacher也是一类学校的成员SchoolMember,研究生GraduateStudent是一类学生Student。

attachment:figure6.png

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

attachment:figure7.png

错误继承的例子:

attachment:figure9.png

attachment:figure10.png

2. 派生类的定义

我们用C++语言来描述学生老师之间的关系:

   1 struct Date {
   2     int year;
   3     int month;
   4     int day;
   5 };
   6 
   7 struct Course {
   8     string name;
   9     int credit;
  10 };
  11 
  12 class SchoolMember {
  13 private:
  14     string name;
  15     Date birth;
  16     bool gender;
  17 };
  18 
  19 //Student类继承了Person类,public指明继承方式是公有继承
  20 class Student : public SchoolMember 
  21 {
  22 private:
  23     string id;
  24     int department;
  25     int credit;
  26     Date enroll_day;
  27     Date graduate_day;
  28 };
  29 
  30 class Teacher; //前置声明
  31 
  32 //GraduateStudent类继承了Student类
  33 class GraduateStudent : public Student {
  34 private:
  35     Teacher *tutor;
  36 };
  37 
  38 //Teacher类继承了SchoolMember类
  39 class Teacher : public SchoolMember 
  40 {
  41 private:
  42     int title;
  43     int salary;
  44     GraduateStudent *supervise[8];
  45     int num_supervise;
  46 };

派生类对象的定义:

   1 int main() {
   2     SchoolMember p;
   3     Student s; //派生类对象s包括了基类的全部成员,还有在派生类中新增的成员
   4     Teacher t;
   5     GraduateStudent g;
   6 }

对象的内存布局:

attachment:figure8.png

加上成员函数的实现:

   1 struct Date {
   2     int year;
   3     int month;
   4     int day;
   5 };
   6 
   7 struct Course {
   8     string name;
   9     int credit;
  10 };
  11 
  12 class SchoolMember {
  13 private:
  14     string name;
  15     Date birth;
  16     bool gender;
  17 public:
  18     Date get_bith() {
  19         return birth;
  20     }
  21     string get_name() {
  22         return name;
  23     }
  24     bool get_gender() {
  25         return gender;
  26     }
  27 };
  28 
  29 //Student类继承了Person类,public指明继承方式是公有继承
  30 class Student : public SchoolMember 
  31 {
  32 private:
  33     string id;
  34     int department;
  35     int credit;
  36     Date enroll_day;
  37     Date graduate_day;
  38 public:
  39     string get_id() {
  40         return id;
  41     }
  42     int get_department() {
  43         return department;
  44     }
  45     int get_credit() {
  46         return credit();
  47     }
  48     Date get_enroll_day() {
  49         return enroll_day;
  50     }
  51     Date get_graduate_day() {
  52         return graduate_day;
  53     }
  54     void enroll(Date day) {
  55         enroll_day = day;
  56     }
  57     void graduate(Date day) {
  58         graduate_day = day;
  59     }
  60 };
  61 //GraduateStudent类继承了Student类
  62 class GraduateStudent : public Student {
  63 private:
  64     Teacher *tutor;
  65 public:
  66     Teacher *get_tutor() {
  67         return tutor;
  68     }
  69     void set_tutor(Teacher *t) {
  70         if(tutor == t) return;
  71         tutor = t;
  72         t->add_supervise(this);
  73     }
  74 };
  75 
  76 //Teacher类继承了SchoolMember类
  77 class Teacher : public SchoolMember 
  78 {
  79 private:
  80     int title;
  81     int salary;
  82     GraduateStudent *supervise[8];
  83     int num_supervise;
  84 public:
  85     void add_supervise(GraduateStudent *g) {
  86         for(int i = 0; i < num_supervise; i++)
  87             if(supervise[i] == g)
  88                 return;
  89         supervise[num_supervise++] = g;
  90         g->set_tutor(this);
  91     }
  92 };

练习:将各种图形类用C++描述出来。

3. 派生类的构造和析构

在创建对象时,我们需要对对象进行初始化。普通类型的对象的初始化是由构造函数完成的,对于派生类对象也是相同的。

创建派生类对象时,派生类的构造函数会被自动调用。

   1 struct Date {
   2     int year, month, day;
   3     Date(int y, int m, int d) {
   4         year = y;
   5         month = m;
   6         day = d;
   7     }
   8 };
   9 
  10 class SchoolMember {
  11 private:
  12     string name;
  13     Date birth
  14     bool gender;
  15 };
  16 
  17 class Student : public SchoolMember {
  18 private:
  19     string id;
  20     int department;
  21     int credit;
  22     Date enroll_day;
  23     Date graduate_day;
  24 public:
  25     Student(string n, Date b, bool g, string i, int d): enroll_day(0,0,0), graduate_day(0,0,0) {
  26         id = i;
  27         department = d;
  28         credit = 0;
  29     }
  30 };
  31 int main() {
  32     Student d("Jack", Date(1985, 9, 13), true, "05011101", 11);
  33 }

这个例子里面,派生类Student扩展的部分已经初始化,但是基类的部分没有初始化。要初始化继承下来的基类部分的成员,可以在基类中定义构造函数。

   1 struct Date {
   2     int year, month, day;
   3     Date(int y, int m, int d) {
   4         year = y;
   5         month = m;
   6         day = d;
   7     }
   8 };
   9 
  10 class SchoolMember {
  11 private:
  12     string name;
  13     Date birth
  14     bool gender;
  15 public:
  16     SchoolMember(string n, Date b, bool g) : birth(b) {
  17         name = n;
  18         gender = g;
  19     }
  20 };
  21 
  22 class Student : public SchoolMember {
  23 private:
  24     string id;
  25     int department;
  26     int credit;
  27     Date enroll_day;
  28     Date graduate_day;
  29 public:
  30     Student(string n, Date b, bool g, string i, int d)
  31         : enroll_day(0,0,0), graduate_day(0,0,0), SchoolMember(n, b, g) {
  32         id = i;
  33         department = d;
  34         credit = 0;
  35     }
  36 };
  37 int main() {
  38     Student d("Jack", Date(1985, 9, 13), true, "05011101" 11);
  39 }

如果基类中有构造函数,派生类没有定义构造函数,或者有构造函数但是没有显示调用基类构造函数,那么基类中的默认构造函数会被自动调用。

   1 class Person {
   2 public: 
   3     Person(string aname = "", bool agender = false) {
   4         name = aname;
   5         gender = agender;
   6         cout << "Person initialized" << endl;
   7     }
   8 private:
   9     string name;
  10     bool gender;
  11 };
  12 class Student : public Person{
  13     int department;
  14     string id;
  15 public:
  16     Student() :department(11), id("0509342") {
  17         cout << "Student initialized" << endl;
  18     }
  19 };
  20 int main() {
  21 }

如果基类的构造函数需要参数,那么需要使用初始化列表为基类构造提供参数。

   1 class Person {
   2 public: 
   3     Person(string aname, bool agender) {
   4         name = aname;
   5         gender = agender;
   6     }
   7 private:
   8     string name;
   9     bool gender;
  10 };
  11 class Student : public Person {
  12 public:
  13     Student(string name, bool gender, int dep) 
  14         : department(dep) , Person ( name, gender )
  15     {
  16     }
  17 private:
  18     int department;
  19 };

在一个多层的继承结构里面,创建一个派生类对象时,所有的基类的构造函数都会被调用。

   1 class Person {
   2 public: 
   3     Person( ) { cout << "construct Person"; }
   4 };
   5 class Student : public Person {
   6 public:
   7     Student( ) { cout << "construct Student"; }
   8 };
   9 class GradStudent : public Student {
  10 public:
  11     GradStudent( ) { cout << "construct GradStudent"; }
  12 };
  13 int main( ) {
  14     GradStudent gs;
  15 }

类的完整实现实例:

   1 class Person {
   2     string name;
   3     bool gender;
   4 public:
   5     Person(string n, bool g) : name(n), gender(g) {
   6     }
   7 };
   8 class Teacher : public Person {
   9     int salary;
  10     int level;
  11 public:
  12     Teacher(string n, bool g, int s, int l) : Person(n, g), salary(s), level(l) {
  13     }
  14 };
  15 class Student: public Person {
  16     string id;
  17     int department;
  18 public:
  19     Student(string n, bool g, string i, int d) : Person(n, g), id(i), department(d) {
  20     }
  21 };
  22 class GradStudent: public Student {
  23     Teacher *tutor;
  24 public:
  25     GradStudent(string n, bool g, string i, int d, Teacher *t)
  26     : Student(n, g, i, d), tutor(t) {
  27     }
  28 };

派生类对象被销毁时,基类的析构函数会被自动调用。如果派生类和基类都有析构函数,那么他们都会被调用。

   1 class Person {
   2 public: 
   3     ~Person() { 
   4         cout <<"destruct Person" <<endl;
   5     }
   6 };
   7 class Student : public Person{
   8 public:
   9     ~Student() {
  10         cout << "destruct Student" << endl;
  11     }
  12 };
  13 int main() {
  14     Student s;
  15     Person p;
  16 }

派生类构造和析构顺序

   1 class Person {
   2 public: 
   3     Person() { cout << "construct Person" << endl; } 
   4     ~Person() { cout << "destruct Person" << endl; }
   5 };
   6 class Student : public Person{
   7 public:
   8     Student() { cout << "construct Student" <<endl; }
   9     ~Student() { cout << "destruct Student" <<endl; }
  10 };
  11 int main() {
  12     Student s;
  13     Person p;
  14 }

派生类析构函数只负责派生类增加部分的分配资源的析构。

   1 class Base {
   2 public: 
   3     Base() { 
   4         s = new char[3];
   5     }
   6     ~Base() {
   7         delete[ ] s;
   8     }
   9 private:
  10     char *s;
  11 };
  12 class Derived : public Base {
  13 public: 
  14     Derived() { 
  15         sd = new char[5]; 
  16     }
  17     ~Derived () {
  18         delete[ ] sd;
  19     }
  20 private:
  21     char *sd;
  22 };
  23 int main() {
  24     Base b;
  25     Derived  d;
  26 }

4. 派生类对象和对象指针的复制

有继承关系的对象复制

   1 Student s("jack", Date(1980, 8, 8), true, 970101, 10);
   2 Person  p = s; //slice
   3 Student  s = p; //error
   4 
   5 Student  *pD = new Student("jack", Date(1980, 8, 8), true, 970101, 10);
   6 Person *pB = pD;  //correct 基类指针指向派生类对象
   7 Person *pB2 = new Student("jack", Date(1980, 8, 8), true, 970101, 10);
   8 pB = new Teacher; //多态性在这里体现
   9 Student &rd = s;
  10 Person &rp = p;
  11 Student &rd2 = p; //error
  12 Person &rp2 = s; // ok
  13 

指针类型的转换

   1 Person  *pB = new Student;
   2 Student *pD2 = pB; //error
   3 Student *pD3 = dynamic_cast<Student *>(pB);
   4 
   5 Person *pB = new Teacher;
   6 Student *pD = dynamic_cast<Student *>(pB); //转换失败,得到空指针
   7 Student *pD2 = static_cast<Student *>(pB); //static_cast转换时不做检查,访问pD2的后果不堪设想
   8 pD2->get_id(); //不可设想的后果
   9 

5. 派生类的访问权限

   1 class Base {
   2 public:
   3     int a;
   4 protected:
   5     int b;
   6 private:
   7     int c;
   8 };
   9 class Derived :public Base {
  10     void f() {
  11         cout << a << b << c;//访问c是错误的
  12     }
  13 };
  14 void f(Derived &d) {
  15     cout << d.a<< d.b<< d.c; //访问b,c都是错的
  16 }

   1 class Base {
   2 protected:
   3     int x;
   4 };
   5 class Derived  : public Base{
   6 public:
   7     void f( ) {
   8         Base b; 
   9         cout << b.x; //访问基类对象的保护成员,错误
  10         cout << x;  //发访问继承的基类保护成员,正确
  11     }
  12 };

使用using声明改变基类成员权限

   1 class Person {
   2 public:
   3     string get_name();
   4     Date get_date();
   5     bool get_gender();
   6 };
   7 class Student : public Person {
   8 private:
   9     using Person::get_name;
  10 };

派生类的成员名字如果与基类相同,将隐藏基类成员

   1 class Person {
   2 public:
   3     void set(string name);
   4     void set(bool gender);
   5 };
   6 class Student : public Person{
   7 public:
   8     void set(int department); // hide base member functions
   9 };
  10 void f(Student &s) {
  11     s.set("12345"); 
  12     s.set( true );
  13     s.Person::set("12345"); //ok
  14     s.Person::set(true); //ok
  15 }

6. 多继承

在C++中允许一个派生类有多个基类,这种继承叫做多继承。相对的只有一个基类的继承被称为单继承。多继承表达的是“派生类既是一种基类A,又是一种基类B”的逻辑关系。

比如Assistent类继承了Student和Teacher两个类:

   1 class Assistant : public Student, public Teacher {
   2 public:
   3   Assistant(string n, string i, string l) 
   4         :  Teacher(n, l), Student(n, i) {
   5     cout << "a";
   6   }
   7   void print() const {
   8     cout<< name << id << level; // ambiguous error
   9     cout<< Student::name << Teacher::name << id << level;
  10   }
  11 };

注意:不同基类中的同名的成员会在派生类中同时存在。比如这里的name,id,level等。

多继承时对象的复制:派生类对象的指针可以赋给任何一个基类类型的指针

   1 Assistant  assist("jack", "12456", "assistant");
   2 Student  s = assist; //OK, slice
   3 Teacher  t = assist; //OK, slice
   4 Student *ps = &assist; //OK
   5 Teacher *pt= &assist; //OK
   6 ps->print();
   7 pt->print();
   8 Person *p = &assist; //ambigius
   9 Person *p = (Student *)&assist;
  10 Person *p = (Teacher *)&assist;
  11 p->print();

7. 虚基类

   1 class Person {
   2 };
   3 class Student : virtual public Person {
   4 };
   5 class Teacher : virtual public Person {
   6 };
   7 class Assistant : public Student, public Teacher {
   8 };

虚基类Person在派生类Assistant的对象中只有一份

   1 class Person {
   2     string name;
   3 public:
   4     Person(string n):name(n){ cout << "P"; }
   5 };
   6 class Student : virtual public Person {
   7     string id;
   8 public:
   9     Student(string n, string i):id(i), Person(n){ cout<<"S";}
  10 };
  11 class Teacher : virtual public Person {
  12     string level;
  13 public:
  14     Teacher(string n, string l):level(l), Person(n){cout<<"T";}
  15 };
  16 class Assistant : public Student, public Teacher {
  17 public:
  18     Assistant(string n, string i, string l)
  19     : Student(n, i), Teacher(n, l), Person(n) {cout<<"A";}
  20 };

8. 保护继承和私有继承

   1 class Derived : private Base {
   2 //…
   3 };
   4 
   5 class Derived: protected Base {
   6 //…
   7 };

   1 class Base {
   2 public: int a;
   3 protected: int b;
   4 private: int c;
   5 };
   6 class Derived : private Base{
   7     void f() {
   8         cout << a << b << c;
   9     }
  10 };
  11 int main() {
  12     Derived d;
  13     cout << d.a << d.b << d.c;
  14 }

9. 多态与虚函数

相同的指令,作用在不同类型的对象上,产生不同动作。 比如:很多动物,有猫、狗、老虎等等。现在让所有的动物做一个“叫”的动作,结果…… 比如:有很多图形,有直线、方形、圆形、椭圆等等。现在让所有的图形把自己画在屏幕上,结果……

   1 int main() {
   2     Person *p[100];
   3     p[0] = new Student(“Jack”, Date(1984, 1,1), false, “20111374”, 11);
   4     p[1] = new Teacher(“Marry”, Date(1969, 5,5), true, “lecturer”, 1000);
   5     p[2] = new Student(“David”, Date(1983, 11,11), false, “20112343”, 11);
   6     //...
   7 }

现在要求写一个函数printall显示所有人的详细信息,应该怎么做?

   1 class Person {
   2     enum PersonType{ P, S,T, G } type; //用来区分学生还是教师
   3 };
   4 void printall( Person *p[100] ) {
   5     for( int i = 0; i < 100; i++) {
   6         cout << p[i]->name << p[i]->birth << p[i]->gender;
   7         switch(p[i]->type) { 
   8         case S: {
   9                 Student *s = (Student*)(p[i]);
  10                 cout << s->id << s->department;
  11             }  break;
  12         case T: {
  13                 Teacher *t = (Teacher*)(p[i]);
  14                 cout << t->level << t->salary;
  15             }  break;
  16         }
  17     }
  18 }

在每个类上增加一个print函数:

   1 class Person {
   2 public:
   3         void print(){ cout << name << birth <<gender; }
   4 };
   5 class Student : public Person{
   6 public:
   7         void print() { 
   8         cout << name << birth << gender << ID << department; 
   9     }
  10 };
  11 class Teacher : public Person {
  12 public:
  13         void print() { 
  14         cout << name << birth << gender << level << salary; 
  15      }
  16 };
  17 void printall( Person* p[100] ) {
  18     for(int i = 0; i < 100; i++) 
  19     switch (p[i]->type) {
  20         case S:
  21         {
  22             Student *s = (Student*)(p[i]);
  23             s->print();
  24         } break;        
  25         case T:
  26         {
  27             Teacher *t = (Teacher*)(p[i]);
  28             t->print();
  29         } break;
  30     }
  31 }

不够简洁,理想的做法是:

   1 void printall(Person *p[100]) {
   2     for ( int i =0; i < 100; i++)
   3         p[i]->print( );
   4 }

但是,这时输出是

Jack 1984-1-1 false
Marry 1969-5-5 true
David 1983-3-3 false
……

虚函数

   1 class Person {
   2 protected:
   3         string name;
   4         Date birth;
   5         bool gender;
   6 public:
   7         virtual void print( ) { //虚函数
   8                 cout << name << birth << gender;
   9         }
  10 };

override虚函数

   1 class Student : public Person {
   2 protected:
   3         string ID;
   4         int department;
   5 public:
   6         virtual void print( ) {  // override Base::print
   7                 cout << name << birth <<gender;
   8                 cout << ID << department;
   9         }
  10 };
  11 class Teacher : public Person{
  12 protected:
  13         int level;
  14         double salary;
  15 public:
  16         virtual void print() { //override Base::print
  17                 cout << name << birth << gender;
  18                 cout << level << salary;
  19         }
  20 };
  21 void printall(Person *p[100]) {
  22     for ( int i =0; i < 100; i++)
  23         p[i]->print( );
  24 }

这时候的结果是:

Jack 1984-1-1 false 20111374 11
Marry 1969-5-5 true lecturer 1000
David 1983-11-11 false 2012343 11
……

调用被override的函数

   1 class Teacher : public Person {
   2 protected:
   3         string level;
   4         double salary;
   5 public:
   6         virtual void print() {
   7                 Person::print();
   8                 cout << level <<  salary;
   9         }
  10 };

多态的限制

   1 int main() {
   2     Person p(“Jack”, Date(1984,1,1), false);
   3     p.print(); // Person::print
   4     Student s(“David”, Date(1983,11,11), false, 403315,10);
   5     s.print();  // Student::print
   6     Person p2 = s;
   7     p2.print(); // Person::print
   8     Person *p3 = &s;
   9     p3->print();  // Student::print 这里是多态
  10     Person &p4 = s;
  11     p4.print();  
  12 }

更多例子:

   1 class Shape {
   2 public: virtual void draw();
   3 };
   4 class Rectangle : public Shape{
   5 public: virtual void draw();
   6 };
   7 class Circle :public Shape {
   8 public: virtual void draw();
   9 };
  10 int main() {
  11   Rectangle r;
  12   Circle c;
  13   Shape &s = r;
  14   s.draw();
  15   Shape &t = c;
  16   t.draw(); 
  17 }

问题:

   1 class Shape {
   2 public: virtual void draw();
   3 //no virtual function in base
   4 };
   5 class Rectangle : public Shape{
   6 public: 
   7 void draw();
   8 };
   9 class Circle :public Shape {
  10 public: 
  11 void draw();
  12 }; 
  13 void f(Shape* s) {
  14     s->draw();
  15 }
  16 int main() {
  17         Shape *s = new Rectangle;
  18     f( s );
  19 }

基类中没有虚函数draw,则s->draw()出错。

问题:

   1 class Shape {
   2 public: virtual void draw();
   3 };
   4 class Rectangle : public Shape{
   5 public: 
   6     virtual void draw(int a);
   7 };
   8 class Circle :public Shape {
   9 public: 
  10     virtual void draw();
  11 }; 
  12 void f(Shape* s) {
  13     s->draw();
  14 }
  15 int main() {
  16         Shape *s = new Rectangle;
  17     f( s );
  18     Shape *c = new Circle;
  19     f( c );
  20 }

Rectangle中定义的draw与基类中的虚函数draw不同,则基类中的draw没有被override。

一个函数调用如果在编译时候确定调用哪个函数,我们称它为静态绑定;如果编译时候不能确定,到运行时才能确定,那么我们称它为动态绑定。

   1 void f(Person *p) {
   2     p->print( ); //static or dynamic?
   3 }

一般虚函数使用动态绑定,普通函数使用静态绑定。动态绑定比静态绑定速度慢。

虚表(virtual table)——C++多态的实现方法

   1 int main() {
   2     Person *p = new Student(...);
   3     delete p;  // which destructor is called?
   4 }

虚析构函数作用:通过基类指针来释放派生类对象时,能够保证调用正确的析构函数

   1 class Person {
   2 public:
   3     virtual ~Person() { };
   4 };

更多例子:动物的叫的例子

   1 class Animal {
   2 public:
   3     virtual void shout() { cout << "animal cannot shout"; }
   4 };
   5 class Dog : public Animal {
   6 public: 
   7     void shout() { cout << “Dog barking!”; }
   8 };
   9 class Ox : public Animal {
  10 public: 
  11     void shout() { cout << “Ox Moo”; }
  12 };
  13 
  14 class Fish: public Animal {
  15 //shout not overridden here, Animal::shout is inherited
  16 };
  17 void f(Animal *a) {
  18      a->shout();
  19 }
  20 int main () {
  21     int i; cin >> i;
  22     Animal *a;
  23     switch(i) {
  24     case 1: a = new Dog; break;
  25     case 2: a = new Ox; break;
  26     case 3: a = new Fish; break;
  27     }
  28     f(a);
  29     delete a;
  30 }

10. 纯虚函数和抽象类

问题:某些基类的虚函数没有实现的意义

   1 class Animal {
   2 public: 
   3     virtual void shout() { cout << "animal cannot shout"; } 
   4 };

纯虚函数(pure virtual function):函数体可以省略的虚函数

   1 class Animal {
   2 public:         
   3     virtual void shout() = 0;  //Pure virtual function
   4 };

抽象类(abstract class):含有纯虚函数的类称为抽象类。类中只要有一个函数是纯虚函数,这个类就是抽象类。抽象类不能实例化,即不能定义对象。

   1 void f() {
   2     Animal a; // error
   3     Animal *pa1 = new Animal; // error
   4     Animal *pa2 = new Ox;
   5     pa2->shout();
   6     Animal *pa3 = new Fish; 
   7     pa3->shout();
   8 }

与抽象类相对的是实体类(concrete class),即不包含纯虚函数的类

抽象类虽然不能够实例化,但可以派生。在派生类中,纯虚函数会被继承。如果在派生类中,给出了所有纯虚函数的实现,那么派生类将成为实体类

   1 class Shape { //abstract class
   2 public: 
   3     virtual void draw() =0;
   4     virtual double getarea() = 0;
   5 };
   6 class Rectangle :public Shape {
   7 public: 
   8     virtual void draw() {/*...*/}
   9     virtual double getarea() { /*...*/}
  10 }; 
  11 class Circle : public Shape{
  12 public: 
  13     virtual void draw() { /*...*/}
  14 }; 

Rectangle为实体类,Circle为抽象类

纯虚函数一般省略函数体,但也可以有函数体

   1 class Person {
   2 public:
   3     virtual void print() = 0 {
   4         cout << name << gender << birth;
   5     }
   6 };

带函数体的纯虚函数可以在派生类中被调用

   1 class Student : public Person{
   2 public:
   3     virtual void print() {
   4         Person::print();  //base pure virtual function is called
   5         cout << id << department;
   6     }
   7 };

接口类:只有纯虚函数成员的类。 IUnknown:微软所有COM(Component Object Model)对象的基类

   1 class IUnknown {
   2 public:
   3     virtual long QueryInterface( 
   4                 REFIID riid, void **ppvObject) = 0;
   5     virtual unsigned long AddRef( void) = 0;
   6     virtual unsigned long Release( void) = 0;
   7 };

11. 运行时类型信息

问题:某些情况下,光靠多态性不够用,必须得到对象准确的类型信息

   1 void f(Shape *s) {
   2     // what on earth is s?
   3 }

两种RTTI机制:dynamic_cast和typeid。先决条件:类中必须有虚函数。在使用RTTI之前,需要先审查在面向对象设计上是否出了问题。

   1 dynamic_cast
   2 void f(Person *p) {
   3     if( dynamic_cast<Student *>(p) ) {
   4        cout<<“p is a Student object or its derivation”;
   5        Student *s = dynamic_cast<Student *>(p);
   6         } else if( dynamic_cast<Teacher *>(p) ) {
   7        cout<<“p is a Teacher object or its derivation”;
   8        Teacher * t = dynamic_cast<Teacher *>(p);
   9     }
  10 }

typeid能够确定表达式的确切类型,使用时要包含<typeinfo>

   1 void f( Shape *s) {
   2     cout << typeid(double).name();
   3     cout << typeid(5280L).name();
   4     cout << typeid(s).name(); //runtime class name
   5     cout << typeid(*s).name(); //static or dynamic?
   6     Shape &r = *s;
   7     cout << typeid(r).name();
   8     Shape  a;
   9     cout << typeid(a).name();
  10 }

typeid返回一个typeinfo类对象的引用

   1 class type_info{
   2 public:
   3     virtual ~type_info();
   4     const char *name() const;
   5     bool operator==(const type_info&) const;
   6     bool operator!=(const type_info&) const;
   7     bool before(const type_info&) const;
   8 private:
   9     type_info( const type_info&);
  10     type_info& operator=(const type_info&);
  11 };

用法:

   1 void f(Shape *s) {
   2     if( typeid( *s ) == typeid( Circle ) )
   3         cout << “s is a Circle”;
   4 }

12. 面向对象设计方法

例子:

设计模式:

前人成功经验总结的可以重复利用的面向对象软件设计的范例。用处:套用前人范式以面向对象思想解决问题,设计出健壮、可复用、支持变化的软件。参考书:《设计模式——可复用面向对象软件的基础》——GoF(Erich Gamma等四人)

The End

ch3n2k.com | Copyright (c) 2004-2020 czk.