As We All Know...
继承和多态是C++语言的两个重要特性,其中,继承实现了派生类对基类的复用,而多态则是通过虚函数的重写来进行。
基本概念
菱形继承是一种比较复杂的继承关系。
比如,学校里的人(Person)主要分为老师(Teacher)和学生(Student),这两者都有人的特性;而其中有的研究生在学校任职,这就
是教助(Assistant),这种身份同时具有老师和学生两类对象的特性,所以,学生、老师、教助就构成了一个菱形关系。
这种继承方式的关系图比较像菱形图案:class Person{public: char _name;};class Student : public Person{public: int _num;};class Teacher : public Person{public: int _id;};class Assistant :virtual public Student, virtual public Teacher{public: char MajorCourse;};其对应的对象模型为:
很明显,Assistant的对象中有两个Person成员,所以菱形继承存在二义性和数据冗余的问题。
解决方法
虚继承--解决菱形继承的二义性和数据冗余的问题。
在继承关系前面加virtual关键字,就成为虚继承。比如:
class Assistant :virtual public Student, virtual public Teacher{public: string MajorCourse;};我们可以看到,变为虚继承前后类的大小有所变化:
为什么加入虚继承后,类对象的大小增加了4个字节呢,
对于虚继承,我们需要知道:
1.%20虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2.%20虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承
体系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。
——>多态
虚函数--类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写--当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态--使用的指针或引用调用重写的虚函数时,指向父类调用的就是父类的虚函数,指向子类则调用子类的虚函数。比如:
class%20Person%20{public:%20%20%20 virtual%20void%20BuyTickets()%20%20 {%20cout%20<<%20"%20买票"%20<<%20endl;%20}PRotected:%20%20 string%20_name;%20%20%20//%20姓名%20};class%20Student%20:%20public%20Person{public: virtual%20void%20BuyTickets()%20 {%20cout%20<<%20"%20买票-半价%20"%20<<%20endl;%20}protected: int%20_num;%20%20%20//学号}; void%20Fun%20(Person&%20p)%20 { p.BuyTickets(); } void%20Test() { Person%20p; Student%20s; Fun(p); Fun(s); }运行结果为:
对于虚函数,我们需要知道:
1. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3. 只有类的成员函数才能定义为虚函数。
4. 静态成员函数不能定义为虚函数。
5. 如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
6. 构造函数不能为虚函数,虽然可以将Operator=定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引
起混淆。
7. 不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
8. 最好把基类的析构函数声明为虚函数。因为尽管派生类的析构函数跟基类的析构函数名称不一样,但是两者构成覆盖,这里是因
为编译器做了特殊处理。
新闻热点
疑难解答
图片精选