25056
备注:
|
← 于2008-02-23 15:35:15修订的的版本55 ⇥
34914
converted to 1.6 markup
|
删除的内容标记成这样。 | 加入的内容标记成这样。 |
行号 1: | 行号 1: |
[[TableOfContents]] | <<TableOfContents>> |
行号 9: | 行号 9: |
重用一个已有的类来创建新的类,一种方法是'''组合'''。比如说构造一个平面图形系统,需要用到点、线段、圆、矩形、三角形等。在线段、圆、矩形、三角形等图形中,都需要表示点这个类型,我们可以通过添加点类型的成员,把它们组合起来。 attachment:figure2.png |
重用一个已有的类来创建新的类,一种方法是'''组合''',它体现'''部分和整体'''的关系。 比如说要构造一辆汽车,汽车是一种复杂的对象,它由很多小的部分组成。 {{attachment:figure4.png}} 再举一个更具体简单的例子:构造一个平面图形系统,需要用到点、线段、圆、矩形、三角形等。在线段、圆、矩形、三角形等图形中,都需要表示点这个类型,比如线段需要有两个端点来表示,我们可以通过添加点类型的成员,把它们组合起来。 {{attachment:figure2.png}} |
行号 15: | 行号 21: |
attachment:figure1.png 再比如说有汽车的例子,汽车是一种复杂的对象,它由很多小的部分组成。 attachment:figure4.png |
{{attachment:figure1.png}} |
行号 23: | 行号 25: |
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,研究生Graduate{{{}}}Student是一类学生Student。 attachment:figure6.png 还有图形之间的关系: attachment:figure7.png 错误的例子: Rectangle继承Point Ellipse继承Circle |
{{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)。 * 逻辑上派生类和基类是一种is a的关系; * 派生类是在基类的基础上添加新的成员进行扩充而得到,它拥有基类的所有成员(所有属性和方法); * 在用到基类对象的地方,可以用派生类对象来代替。 比如说,学生Student是一类学校的成员{{{SchoolMember}}},老师Teacher也是一类学校的成员{{{SchoolMember}}},研究生{{{GraduateStudent}}}是一类学生Student。 {{attachment:figure6.png}} 还有各种图形之间的关系: {{attachment:figure7.png}} 不恰当使用继承的例子: * Rectangle继承Point? {{attachment:figure9.png}} * Ellipse继承Circle还是Circle继承Ellipse? {{attachment:figure10.png}} |
行号 49: | 行号 55: |
我们用C++语言来描述类之间派生和继承的关系: | 我们用C++语言来描述学生老师之间的关系: |
行号 69: | 行号 75: |
//Teacher类继承了SchoolMember类 class Teacher : public SchoolMember { private: int title; int salary; }; //Student类继承了Person类,public指明继承方式是公有继承 |
//Student类继承了SchoolMember类,public指明继承方式是公有继承 |
行号 86: | 行号 85: |
class Teacher; //前置声明 |
|
行号 91: | 行号 93: |
//Teacher类继承了SchoolMember类 class Teacher : public SchoolMember { private: int title; int salary; GraduateStudent *supervise[8]; int num_supervise; }; |
|
行号 97: | 行号 109: |
Student s; //派生类对象s包括了基类的全部成员,还有在派生类中新增的成员 | Student s; |
行号 105: | 行号 117: |
attachment:figure8.png | {{attachment:figure8.png}} |
行号 108: | 行号 120: |
{{{#!cplusplus 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++描述出来。 == 派生类的构造和析构 == 在创建对象时,我们需要对对象进行初始化。普通类型的对象的初始化是由构造函数完成的,对于派生类对象也是相同的。 创建派生类对象时,派生类的构造函数会被自动调用。 {{{#!cplusplus 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扩展的部分已经初始化,但是基类的部分没有初始化。要初始化继承下来的基类部分的成员,可以在基类中定义构造函数。 {{{#!cplusplus 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); } }}} 如果基类中有构造函数,派生类没有定义构造函数,或者有构造函数但是没有显示调用基类构造函数,那么基类中的默认构造函数会被自动调用。 |
|
行号 114: | 行号 310: |
string get_name(); Date get_date(); bool get_gender(); }; |
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; } }}} 在一个多层的继承结构里面,创建一个派生类对象时,基类、基类的基类、基类的基类的基类等的构造函数都会被调用。 {{{#!cplusplus 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); } }}} 类的完整实现实例: {{{#!cplusplus 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; |
行号 119: | 行号 396: |
int salary; | |
行号 120: | 行号 398: |
int salary; public: int get_salary(); int get_level(); void add_pupil(GraduateStudent *); void remove_pupil(GraduateStudent *); |
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); } |
行号 134: | 行号 419: |
string get_id(); int get_department(); void take_course(Course *); void enroll(); void graduate(); }; class GradStudent: public Student { |
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 { |
行号 143: | 行号 436: |
Teacher *get_tutor(); void set_tutor(Teacher *); }; }}} |
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); } }; }}} 派生类对象被销毁时,基类的析构函数会被自动调用。如果派生类和基类都有析构函数,那么他们都会被调用。 {{{#!cplusplus 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; } }}} 派生类析构函数只负责派生类增加部分的分配资源的析构。 {{{#!cplusplus 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); } }}} == 类型兼容性 == 在需要使用基类对象的情况下,可以用派生类对象来代替。分为三种情况: * 派生类对象取代基类对象,派生类对象被切割 * 派生类对象指针取代基类对象指针 * 派生类对象引用取代基类对象引用 {{{#!cplusplus 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 } }}} 指针类型的强制转换 {{{#!cplusplus 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(); //不可设想的后果 } }}} |
行号 222: | 行号 622: |
继承和组合是软件复用的两种方式。 == 派生类的构造和析构 == 创建派生类对象时,基类的构造函数会被自动调用,用于初始化继承下来的基类部分的成员。如果派生类没有构造函数,或者没有显示调用,那么基类中的默认构造函数会被自动调用。 {{{#!cplusplus class Person { public: Person(string aname = "", bool agender = false) { name = aname; gender = agender; } private: string name; bool gender; }; class Student : public Person { private: int department; string id; }; int main() { Student d; // default constructor of Person is called } }}} 派生类可以定义自己的构造函数,用于初始化派生类扩展的部分 {{{#!cplusplus class Person { public: Person(string aname = "", bool agender = false) { name = aname; gender = agender; cout << "Person initialized" << endl; } private: string name; bool gender; }; class Student : public Person{ int department; string id; public: Student() :department(11), id("0509342") { cout << "Student initialized" << endl; } }; int main() { } }}} 在这个派生类的构造函数中,没有给基类的构造函数任何参数,基类中的默认构造函数会被调用。 如果基类的构造函数需要参数,那么需要使用初始化列表为基类构造提供参数。 {{{#!cplusplus class Person { public: Person(string aname, bool agender) { name = aname; gender = agender; } private: string name; bool gender; }; class Student : public Person { public: Student(string name, bool gender, int dep) : department(dep) , Person ( name, gender ) { } private: int department; }; }}} 在一个多层的继承结构里面,创建一个派生类对象时,所有的基类的构造函数都会被调用。 {{{#!cplusplus class Person { public: Person( ) { cout << "construct Person"; } }; class Student : public Person { public: Student( ) { cout << "construct Student"; } }; class GradStudent : public Student { public: GradStudent( ) { cout << "construct GradStudent"; } }; int main( ) { GradStudent gs; } }}} 类的完整实现实例: {{{#!cplusplus class Person { string name; bool gender; public: Person(string n, bool g) : name(n), gender(g) { } }; class Teacher : public Person { int salary; int level; public: Teacher(string n, bool g, int s, int l) : Person(n, g), salary(s), level(l) { } }; class Student: public Person { string id; int department; public: Student(string n, bool g, string i, int d) : Person(n, g), id(i), department(d) { } }; class GradStudent: public Student { Teacher *tutor; public: GradStudent(string n, bool g, string i, int d, Teacher *t) : Student(n, g, i, d), tutor(t) { } }; }}} 派生类对象被销毁时,基类的析构函数会被自动调用。如果派生类和基类都有析构函数,那么他们都会被调用。 {{{#!cplusplus class Person { public: ~Person() { cout <<"destruct Person" <<endl; } }; class Student : public Person{ public: ~Student() { cout << "destruct Student" << endl; } }; int main() { Student s; Person p; } }}} 派生类构造和析构顺序 {{{#!cplusplus class Person { public: Person() { cout << "construct Person" << endl; } ~Person() { cout << "destruct Person" << endl; } }; class Student : public Person{ public: Student() { cout << "construct Student" <<endl; } ~Student() { cout << "destruct Student" <<endl; } }; int main() { Student s; Person p; } }}} 派生类析构函数只负责派生类增加部分的分配资源的析构。 |
保护继承和私有继承 {{{#!cplusplus class Derived : private Base { //… }; class Derived: protected Base { //… }; }}} |
行号 390: | 行号 635: |
public: Base() { s = new char[3]; } ~Base() { delete[ ] s; } private: char *s; }; class Derived : public Base { public: Derived() { sd = new char[5]; } ~Derived () { delete[ ] sd; } private: char *sd; }; int main() { Base b; Derived d; } }}} == 派生类对象和对象指针的复制 == 有继承关系的对象复制 {{{#!cplusplus Student s("jack", Date(1980, 8, 8), true, 970101, 10); Person p = s; //slice Student s = p; //error Student *pD = new Student("jack", Date(1980, 8, 8), true, 970101, 10); Person *pB = pD; //correct 基类指针指向派生类对象 Person *pB2 = new Student("jack", Date(1980, 8, 8), true, 970101, 10); pB = new Teacher; //多态性在这里体现 Student &rd = s; Person &rp = p; Student &rd2 = p; //error Person &rp2 = s; // ok }}} 指针类型的转换 {{{#!cplusplus Person *pB = new Student; Student *pD2 = pB; //error Student *pD3 = dynamic_cast<Student *>(pB); Person *pB = new Teacher; Student *pD = dynamic_cast<Student *>(pB); //转换失败,得到空指针 Student *pD2 = static_cast<Student *>(pB); //static_cast转换时不做检查,访问pD2的后果不堪设想 pD2->get_id(); //不可设想的后果 }}} |
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; } }}} |
行号 483: | 行号 689: |
== 虚基类 == | 虚基类 |
行号 518: | 行号 724: |
== 保护继承和私有继承 == {{{#!cplusplus class Derived : private Base { //… }; class Derived: protected Base { //… }; }}} {{{#!cplusplus 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; } }}} |
完整例子: {{{#!cplusplus #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; } }}} = 多态 = |
行号 552: | 行号 927: |
Person *p[100]; | SchoolMember *p[100]; |
行号 561: | 行号 936: |
class Person { enum PersonType{ P, S,T, G } type; //用来区分学生还是教师 }; void printall( Person *p[100] ) { |
class SchoolMember { enum MemberType{ P, S, T, G } type; //用来区分学生还是教师 }; void printall( SchoolMember *p[100] ) { |
行号 583: | 行号 958: |
class Person { | class SchoolMember { |
行号 587: | 行号 962: |
class Student : public Person{ public: void print() { |
class Student : public SchoolMember{ public: void print() { |
行号 595: | 行号 970: |
void print() { | void print() { |
行号 597: | 行号 972: |
} | } |
行号 605: | 行号 980: |
s->print(); | s->print(); |
行号 610: | 行号 985: |
t->print(); | t->print(); |
行号 616: | 行号 991: |
不够简洁,理想的做法是: | 仍不够简洁,理想的做法是: |
行号 635: | 行号 1010: |
string name; Date birth; bool gender; public: virtual void print( ) { //虚函数 cout << name << birth << gender; } }; }}} override虚函数 |
string name; Date birth; bool gender; public: virtual void print( ) { //虚函数 cout << name << birth << gender; } }; }}} 在派生类中覆盖(override)虚函数 |
行号 649: | 行号 1024: |
string ID; int department; public: virtual void print( ) { // override Base::print cout << name << birth <<gender; cout << ID << department; } |
string ID; int department; public: virtual void print( ) { // override Base::print cout << name << birth <<gender; cout << ID << department; } |
行号 659: | 行号 1034: |
int level; double salary; public: virtual void print() { //override Base::print cout << name << birth << gender; cout << level << salary; } |
int level; double salary; public: virtual void print() { //override Base::print cout << name << birth << gender; cout << level << salary; } |
行号 682: | 行号 1057: |
调用被override的函数 | 调用被覆盖(override)的函数 |
行号 686: | 行号 1061: |
string level; double salary; public: virtual void print() { Person::print(); cout << level << salary; } |
string level; double salary; public: virtual void print() { Person::print(); cout << level << salary; } |
行号 700: | 行号 1075: |
p.print(); // Person::print | p.print(); // 这里调用Person::print |
行号 702: | 行号 1077: |
s.print(); // Student::print | s.print(); // 这里调用 Student::print |
行号 704: | 行号 1079: |
p2.print(); // Person::print | p2.print(); // 这里调用 Person::print |
行号 706: | 行号 1081: |
p3->print(); // Student::print 这里是多态 | p3->print(); // 这里调用 Student::print 这里才是多态 |
行号 721: | 行号 1096: |
行号 725: | 行号 1099: |
public: virtual void draw(); | public: virtual void draw(); |
行号 728: | 行号 1103: |
public: virtual void draw(); | public: virtual void draw(); |
行号 731: | 行号 1107: |
public: virtual void draw(); }; int main() { Rectangle r; Circle c; Shape &s = r; s.draw(); Shape &t = c; t.draw(); |
public: virtual void draw(); }; int main() { Rectangle r; Circle c; Shape &s = r; s.draw(); Shape &t = c; t.draw(); |
行号 746: | 行号 1123: |
public: virtual void draw(); //no virtual function in base |
public: //no virtual function in base |
行号 751: | 行号 1128: |
void draw(); | virtual void draw() { /*....*/ } |
行号 755: | 行号 1132: |
void draw(); | virtual void draw() { /*....*/ } |
行号 761: | 行号 1138: |
Shape *s = new Rectangle; | Shape *s = new Rectangle; |
行号 770: | 行号 1147: |
public: virtual void draw(); | public: virtual void draw() {/*....*/} |
行号 774: | 行号 1152: |
virtual void draw(int a); | virtual void draw(int a){/*....*/} |
行号 778: | 行号 1156: |
virtual void draw(); | virtual void draw() {/*....*/} |
行号 784: | 行号 1162: |
Shape *s = new Rectangle; | Shape *s = new Rectangle; |
行号 801: | 行号 1179: |
虚表(virtual table)——C++多态的实现方法 |
虚表(virtual table)——C++实现多态的方法 {{attachment:figure14.png}} 虚析构函数 |
行号 826: | 行号 1205: |
void shout() { cout << “Dog barking!”; } | void shout() { cout << "Dog barking!"; } |
行号 830: | 行号 1209: |
void shout() { cout << “Ox Moo”; } | void shout() { cout << "Ox Moo"; } |
行号 948: | 行号 1327: |
cout<<“p is a Student object or its derivation”; | cout<<"p is a Student object or its derivation"; |
行号 950: | 行号 1329: |
} else if( dynamic_cast<Teacher *>(p) ) { cout<<“p is a Teacher object or its derivation”; |
} else if( dynamic_cast<Teacher *>(p) ) { cout<<"p is a Teacher object or its derivation"; |
行号 989: | 行号 1368: |
cout << “s is a Circle”; | cout << "s is a Circle"; |
行号 1001: | 行号 1380: |
{{attachment:figure11.png}} | |
行号 1002: | 行号 1382: |
{{attachment:figure12.png}} 交互图 {{attachment:figure13.png}} |
继承
1. 继承和派生的含义
如果要提高程序开发的效率,需要对已有的代码进行重用。在面向过程的语言中,重用的单位是函数(过程)。而在面向对象的语言中,重用的单位是类。
重用一个已有的类来创建新的类,一种方法是组合,它体现部分和整体的关系。
比如说要构造一辆汽车,汽车是一种复杂的对象,它由很多小的部分组成。
再举一个更具体简单的例子:构造一个平面图形系统,需要用到点、线段、圆、矩形、三角形等。在线段、圆、矩形、三角形等图形中,都需要表示点这个类型,比如线段需要有两个端点来表示,我们可以通过添加点类型的成员,把它们组合起来。
或者可以把它们之间的关系更明确的表示为:
再比如说一个学生类。
继承(派生)是类的重用的另一种方法,它体现类之间的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?
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类继承了SchoolMember类,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 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类继承了SchoolMember类,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)
26 : enroll_day(0,0,0), graduate_day(0,0,0) {
27 id = i;
28 department = d;
29 credit = 0;
30 }
31 };
32 int main() {
33 Student d("Jack", Date(1985, 9, 13), true, "05011101", 11);
34 }
这个例子里面,派生类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 SchoolMember {
2 string name;
3 Date birth;
4 bool gender;
5 public:
6 SchoolMember(string n = "", Date b = Date(0,0,0), bool g = false)
7 : birth(b) {
8 name = n;
9 gender = g;
10 }
11 };
12 class Student : public SchoolMember{
13 string id;
14 int department;
15 int credit;
16 Date enroll_day;
17 Date graduate_day;
18 };
19 int main() {
20 Student s;
21 }
在一个多层的继承结构里面,创建一个派生类对象时,基类、基类的基类、基类的基类的基类等的构造函数都会被调用。
1 class SchoolMember {
2 private:
3 string name;
4 Date birth
5 bool gender;
6 public:
7 SchoolMember(string n, Date b, bool g) : birth(b) {
8 name = n;
9 gender = g;
10 }
11 };
12
13 class Student : public SchoolMember {
14 private:
15 string id;
16 int department;
17 int credit;
18 Date enroll_day;
19 Date graduate_day;
20 public:
21 Student(string n, Date b, bool g, string i, int d)
22 : enroll_day(0,0,0), graduate_day(0,0,0), SchoolMember(n, b, g) {
23 id = i;
24 department = d;
25 credit = 0;
26 }
27 };
28 class GraduateStudent: public Student {
29 Teacher *tutor;
30 public:
31 GraduateStudent(string n, Date b, bool g, string i, int d, Teacher *t)
32 :Student(n, b, g, i, d) {
33 tutor = t;
34 }
35 };
36 int main() {
37 Teacher teacher;
38 GraduateStudent d("Rose", Date(1985, 9, 13), false, "05011101", 11, &teacher);
39 }
类的完整实现实例:
1 class Date {
2 public:
3 int year, month, day;
4 Date(int y, int m, int d)
5 : year(y), month(m), day(d) {
6 }
7 };
8 struct Course{
9 string name;
10 int credit;
11 };
12
13 class SchoolMember {
14 string name;
15 Date birth;
16 bool gender;
17 public:
18 SchoolMember(string n, Date b, bool g)
19 : name(n), birth(b), gender(g) {
20 }
21 };
22 class GraduateStudent;
23 class Teacher : public SchoolMember {
24 int salary;
25 int title;
26 int num_supervise;
27 GraduateStudent *supervise[8];
28 public:
29 Teacher(string n, Date b, bool g, int t, int s)
30 : SchoolMember(n, b, g), salary(s), title(t) {
31 }
32 void add_supervise(GraduateStudent *g) {
33 for(int i = 0; i < num_supervise; i++)
34 if(supervise[i] == g)
35 return;
36 supervise[num_supervise++] = g;
37 g->set_tutor(this);
38 }
39 };
40 class Student: public SchoolMember {
41 string id;
42 int department;
43 int credit;
44 Date enroll_day;
45 Date graduate_day;
46 public:
47 SchoolMember(string n, Date b, bool g, string i, int d)
48 : SchoolMember(n, b, g), id(i), department(d), credit(0),
49 enroll_day(0,0,0), graduate_day(0,0,0) {
50 }
51 void take_course(Course *c) {
52 credit += c->credit;
53 }
54 void enroll(Date day) {
55 enroll_day = day;
56 }
57 void graduate(Date day) {
58 graduate_day = day;
59 }
60 };
61 class GraduateStudent: public Student {
62 Teacher *tutor;
63 public:
64 GraduateStudent(string n, Date b, bool g, string i, int d, Teacher *t)
65 : Student(n, b, g, i, d), tutor(t) {
66 t->add_supervise(this);
67 }
68 void set_tutor(Teacher *t) {
69 if(tutor == t) return;
70 tutor = t;
71 tutor->add_supervise(this);
72 }
73 };
派生类对象被销毁时,基类的析构函数会被自动调用。如果派生类和基类都有析构函数,那么他们都会被调用。
1 class SchoolMember {
2 public:
3 SchoolMember() {
4 cout << "construct SchoolMember" << endl;
5 }
6 ~SchoolMember() {
7 cout <<"destruct SchoolMember" <<endl;
8 }
9 };
10 class Student : public SchoolMember{
11 public:
12 Student() {
13 cout << "construct Student" << endl;
14 }
15 ~Student() {
16 cout << "destruct Student" << endl;
17 }
18 };
19 int main() {
20 Student s;
21 SchoolMember p;
22 }
派生类析构函数只负责派生类增加部分的分配资源的析构。
1 int MAX_SUPERVISE[] = {0, 0, 4, 10};
2
3 class Teacher : public SchoolMember {
4 int salary;
5 int title;
6 int num_supervise;
7 int capacity_supervise;
8 GraduateStudent **supervise;
9 public:
10 Teacher(string n, Date b, bool g, int t, int s)
11 : SchoolMember(n, b, g), salary(s), title(t) {
12 }
13 Teacher(string n, Date b, bool g, int t, int s)
14 :SchoolMember(n,b,g), salary(s), title(t) {
15 capacity_supervise = MAX_SUPERVISE[t];
16 supervise = new GraduateStudent*[capacity_supervise]
17 }
18 ~Teacher(){
19 delete[] supervise;
20 }
21 void add_supervise(GraduateStudent *g) {
22 for(int i = 0; i < num_supervise; i++)
23 if(supervise[i] == g)
24 return;
25 if(num_supervise >= capacity_supervise)
26 return;
27 supervise[num_supervise++] = g;
28 g->set_tutor(this);
29 }
30 };
31 int main() {
32 Teacher t("czk", Date(1979,8,27), true, 1, 1500);
33 }
4. 类型兼容性
在需要使用基类对象的情况下,可以用派生类对象来代替。分为三种情况:
- 派生类对象取代基类对象,派生类对象被切割
- 派生类对象指针取代基类对象指针
- 派生类对象引用取代基类对象引用
1 int main() {
2 Student s("jack", Date(1980, 8, 8), true, "970101", 10);
3 SchoolMember p = s; //slice
4 Student s2 = p; //error
5
6 Student *pD = new Student("jack", Date(1980, 8, 8), true, "970101", 10);
7 SchoolMember *pB = pD; //correct 基类指针指向派生类对象
8 SchoolMember *pB2 = new Student("jack", Date(1980, 8, 8), true, "970101", 10);
9 pB = new Teacher("jack", Date(1980, 8, 8), true, 4, 1500); //多态性在这里体现
10
11 Student &rd = s;
12 SchoolMember &rp = p;
13 Student &rd2 = p; //error
14 SchoolMember &rp2 = s; // ok
15 }
指针类型的强制转换
1 int main() {
2 SchoolMember *pB = new Student;
3 Student *pD2 = pB; //error
4 Student *pD3 = dynamic_cast<Student *>(pB);
5
6 SchoolMember *pB = new Teacher;
7 Student *pD = dynamic_cast<Student *>(pB); //转换失败,得到空指针
8 Student *pD2 = static_cast<Student *>(pB); //static_cast转换时不做检查,访问pD2的后果不堪设想
9 pD2->enroll(); //不可设想的后果
10 }
5. 派生类的访问权限
- private成员:只在定义它的类的成员函数中可以访问,在派生类的成员函数中不可访问。基类的私有成员被派生类继承,但是不能被派生类访问
- protected成员:在本类及派生类中能访问的成员。派生类可以访问继承的保护成员,但是不能访问一个基类对象的保护成员
使用using声明改变基类成员权限
派生类的成员名字如果与基类相同,将隐藏基类成员
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两个类:
注意:不同基类中的同名的成员会在派生类中同时存在。比如这里的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();
虚基类
虚基类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 };
完整例子:
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 struct Date {
6 int year;
7 int month;
8 int day;
9 public:
10 Date(int y, int m, int d) {
11 year = y;
12 month = m;
13 day = d;
14 }
15 void print() {
16 cout << year<<"-" <<month<<"-" <<day;
17 }
18 };
19
20 const bool MALE = false;
21 const bool FEMALE = true;
22
23 enum {SCHOOLMEMBER, TEACHER, STUDENT, GRADUATESTUDENT};
24
25 class SchoolMember {
26 string name;
27 Date birth;
28 bool gender;
29 public:
30 int type;
31
32 SchoolMember(string n, Date b, bool g)
33 : birth(b) {
34 name = n;
35 gender = g;
36 type = SCHOOLMEMBER;
37 }
38 string get_name() {
39 return name;
40 }
41 Date get_birth() {
42 return birth;
43 }
44 bool get_gender() {
45 return gender;
46 }
47 virtual void print() {
48 cout << name;
49 birth.print();
50 cout << (gender==MALE?"MALE":"FEMALE") <<endl;
51 }
52 virtual ~SchoolMember() {
53 cout << "destruct SchoolMember " << get_name() << endl;
54 }
55 };
56
57 struct Course {
58 string name;
59 int credit;
60 };
61
62 enum { MATHS = 8, PHYSICS =9, CS = 10};
63
64 class Student : virtual public SchoolMember {
65 string id;
66 int department;
67 int credit;
68 public:
69 Student(string n, Date b, bool g, string i, int d)
70 : SchoolMember(n,b,g)
71 {
72 id = i;
73 department = d;
74 credit = 0;
75 type = STUDENT;
76 }
77 void take_course(Course *c){
78 credit += c->credit;
79 }
80 bool can_graduate() {
81 return credit >= 160;
82 }
83 string get_id() {
84 return id;
85 }
86 int get_credit() {
87 return credit;
88 }
89 int get_department() {
90 return department;
91 }
92
93 virtual void print() {
94 cout << get_name();
95 get_birth().print();
96 cout << (get_gender()==MALE?"MALE":"FEMALE") << id << credit <<department << endl;
97 }
98 ~Student() {
99 cout << "destruct Student:" << get_name() << endl;
100 }
101
102 };
103
104 class Graduate;
105
106 enum {PROFESSOR =4, VICEPROFESSOR = 3, ASSISTANT = 1};
107 class Teacher :virtual public SchoolMember {
108 int title;
109 int salary;
110 Graduate* supervised[10];
111 int num_supervised;
112 public:
113 Teacher(string n, Date b, bool g, int t, int s)
114 : SchoolMember(n, b, g) {
115 title = t;
116 salary = s;
117 num_supervised = 0;
118 type = TEACHER;
119 }
120 void add_supervised(Graduate *g) {
121 supervised[num_supervised] = g;
122 num_supervised++;
123 }
124 virtual void print() {
125 cout << get_name();
126 get_birth().print();
127 cout << (get_gender()==MALE?"MALE":"FEMALE") << title << salary << endl;
128 }
129 ~Teacher() {
130 cout << "destruct Teacher:" << get_name() << endl;
131 }
132
133 };
134
135 class Graduate :public Student {
136 Teacher *tutor;
137 public:
138 Graduate(string n, Date b, bool g, string i, int d, Teacher *t)
139 : Student(n, b, g, i, d), SchoolMember(n, b,g)
140 {
141 tutor = t;
142 type = GRADUATESTUDENT;
143 }
144 void set_tutor(Teacher *t){
145 tutor = t;
146 }
147 virtual void print() {
148 cout << get_name();
149 get_birth().print();
150 cout << (get_gender()==MALE?"MALE":"FEMALE") << get_id() << get_credit()<< get_department() << tutor->get_name() << endl;
151 }
152 ~Graduate() {
153 cout << "destruct Graduate:" << get_name() << endl;
154 }
155
156 };
157
158 class Assistant : public Student, public Teacher {
159 public:
160 Assistant(string n, Date b, bool g, string i, int d, int t, int s)
161 :Student(n, b, g, i, d), Teacher(n, b, g, t, s),
162 SchoolMember(n, b, g) {
163
164 }
165 void print() {
166 }
167 };
168
169 void printall(SchoolMember *p[], int n) {
170 for(int i = 0; i < n; i++) {
171 p[i]->print();
172 }
173 }
174
175 int main() {
176 SchoolMember *members[100];
177
178 members[0] = new SchoolMember("Jack", Date(1980, 1, 1), MALE);
179 members[1] = new Student("Mike", Date(1985, 5, 5), MALE, "12345", CS);
180 members[2] = new Teacher("Rose", Date(1970, 3, 3), FEMALE, PROFESSOR, 2000);
181 members[3] = new Graduate("Tom", Date(1982, 2, 2), MALE, "54321",
182 CS, dynamic_cast<Teacher*>(members[2]));
183
184 printall(members, 4);
185 for(int i = 0;i < 4; i++)
186 delete members[i];
187 Assistant a("Jeff", Date(1978, 5, 5), MALE, "11100", CS, ASSISTANT, 800);
188 SchoolMember *s = &a;
189
190 }
多态
1. 多态与虚函数
相同的指令,作用在不同类型的对象上,产生不同动作。 比如:很多动物,有猫、狗、老虎等等。现在让所有的动物做一个“叫”的动作,结果…… 比如:有很多图形,有直线、方形、圆形、椭圆等等。现在让所有的图形把自己画在屏幕上,结果……
现在要求写一个函数printall显示所有人的详细信息,应该怎么做?
1 class SchoolMember {
2 enum MemberType{ P, S, T, G } type; //用来区分学生还是教师
3 };
4 void printall( SchoolMember *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 SchoolMember {
2 public:
3 void print(){ cout << name << birth <<gender; }
4 };
5 class Student : public SchoolMember{
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 }
仍不够简洁,理想的做法是:
但是,这时输出是
Jack 1984-1-1 false Marry 1969-5-5 true David 1983-3-3 false ……
虚函数
在派生类中覆盖(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 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 }
- 多态只能通过指针或者引用来实现。
- 非成员函数、类的构造函数、静态成员函数不能是虚函数
- virtual写在成员函数的声明中,而不是定义中
- 一般情况下,派生类要override基类的虚函数,要求函数声明形式完全一样。
例外:基类的虚函数返回基类的指针或者引用,那么派生类override它,可以返回此派生类的指针或引用
class Base { virtual Base*f(); }; class Derived:public Base { virtual Derived* f(); };
- 如果派生类没有override基类的虚函数,那么派生类会继承基类的虚函数
更多例子:
1 class Shape {
2 public:
3 virtual void draw();
4 };
5 class Rectangle : public Shape{
6 public:
7 virtual void draw();
8 };
9 class Circle :public Shape {
10 public:
11 virtual void draw();
12 };
13 int main() {
14 Rectangle r;
15 Circle c;
16 Shape &s = r;
17 s.draw();
18 Shape &t = c;
19 t.draw();
20 }
问题:
1 class Shape {
2 public:
3 //no virtual function in base
4 };
5 class Rectangle : public Shape{
6 public:
7 virtual void draw() { /*....*/ }
8 };
9 class Circle :public Shape {
10 public:
11 virtual 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:
3 virtual void draw() {/*....*/}
4 };
5 class Rectangle : public Shape{
6 public:
7 virtual void draw(int a){/*....*/}
8 };
9 class Circle :public Shape {
10 public:
11 virtual void draw() {/*....*/}
12 };
13 void f(Shape* s) {
14 s->draw();
15 }
16 int main() {
17 Shape *s = new Rectangle;
18 f( s );
19 Shape *c = new Circle;
20 f( c );
21 }
Rectangle中定义的draw与基类中的虚函数draw不同,则基类中的draw没有被override。
一个函数调用如果在编译时候确定调用哪个函数,我们称它为静态绑定;如果编译时候不能确定,到运行时才能确定,那么我们称它为动态绑定。
一般虚函数使用动态绑定,普通函数使用静态绑定。动态绑定比静态绑定速度慢。
虚表(virtual table)——C++实现多态的方法
虚析构函数
虚析构函数作用:通过基类指针来释放派生类对象时,能够保证调用正确的析构函数
更多例子:动物的叫的例子
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 }
2. 纯虚函数和抽象类
问题:某些基类的虚函数没有实现的意义
纯虚函数(pure virtual function):函数体可以省略的虚函数
抽象类(abstract class):含有纯虚函数的类称为抽象类。类中只要有一个函数是纯虚函数,这个类就是抽象类。抽象类不能实例化,即不能定义对象。
与抽象类相对的是实体类(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为抽象类
纯虚函数一般省略函数体,但也可以有函数体
带函数体的纯虚函数可以在派生类中被调用
接口类:只有纯虚函数成员的类。 IUnknown:微软所有COM(Component Object Model)对象的基类
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>
typeid返回一个typeinfo类对象的引用
用法:
4. 面向对象设计方法
- 确定程序中需要的类及每个类需要的操作
- 将不同类的共同的操作提取到基类
- 通过基类接口来操纵各种不同的对象
- 可以通过增加新的派生类来扩展系统
例子:
- 复合文档(composite模式):包含文字、图形的文档
- 一组数据,有多种表现形式(observer模式)(比如表格、柱状图、柄图)
- 交互图
设计模式:
前人成功经验总结的可以重复利用的面向对象软件设计的范例。用处:套用前人范式以面向对象思想解决问题,设计出健壮、可复用、支持变化的软件。参考书:《设计模式——可复用面向对象软件的基础》——GoF(Erich Gamma等四人)