以下为本人大一时阅读《C++ Primer Plus》中关于继承章节所做的笔记
继承:inheritance
现有的类称为“基类”,继承实现的新类称为“派生类”。(在Java、C#中,基类称为超类,派生类称为子类)
派生类的对象是基类的对象,但基类的对象不是派生类的对象
is-a关系:继承
has-a关系:组成
继承关系定义;
例:类TwoDimensionalShape是由类Shape派生而来的(public继承)
class TwoDimensionalShape : public Shape; |
在各种形式的继承中,基类的private成员都不能被它的派生类直接访问,但是这些private基类成员仍得到了继承(即它们仍被视为派生类的一部分)
构造函数和析构函数不能继承,每个类都应该提供特定于自己的构造函数、析构函数
C++要求派生类构造函数调用其基类的构造函数来初始化继承到派生类的基类的数据成员。
例:设类B是类A的一个派生类,若类A已定义了构造函数,则类B的构造函数:
B::B(int a,int b,...) : A(a , b , ...)//调用A的构造函数 { ... } |
如果类B的构造函数没有显式地调用类A的构造函数,C++会尝试隐式地调用类A的默认构造函数,但是因为A没有默认构造函数,编译器将会发出一条错误信息
注意:类B不能访问基类A的private数据,应该通过类A提供的获取函数(get…())来访问类A的private数据成员
在派生类的头文件中使用#include包含基类的头文件:
原因:
1.告诉编译器这个基类是存在的
2.编译器需要使用类定义来决定类对象的大小
3.编译器可以据此判断派生类是否正确地使用了由基类继承而来的成员
protected成员:
为了使派生类可以直接访问基类中的数据成员,可以将基类中的数据成员声明为protected。基类的protected成员既可以在基类的成员和友元访问,又可以被由基类派生的任何类的成员和友元访问。
直接访问protected数据成员可以免去调用设置和获取成员函数的开销
protected数据将产生的问题:
1.若派生类对象不必使用protected数据成员的值,这时派生类可以很容易将无效的值赋给基类的protected数据,导致对象处于不一致的状态。
2.派生类成员函数实现可能太依赖基类的实现。派生类应该只依赖基类提供的服务(也就是非private成员函数),而不应该依赖于基类的实现。当使用了基类的protected数据时,如果修改了基类的实现,那么同时可能需要修改该基类的所有派生类(例如修改protected数据的变量名)(因此,利用成员函数访问数据成员的值时要修改基类的private数据时只需修改直接操作这些数据成员的获取和设置成员函数即可)
由已重新定义基类成员函数的派生类调用基类的函数时要在基类成员函数之前加基类名和二元作用域分辨运算符(::)
先执行基类的构造函数,再执行派生类的构造函数
析构函数的调用顺序与构造函数的执行顺序相反
派生类永远不能直接访问基类的private成员,但是可以通过调用基类的public和protected成员进行间接访问。
附加:
在作业中遇到的关于组合的问题:若要在一个类中添加另一个类的类型作为数据成员,如何初始化?
例:在类Student中将一个类Date的成员作为数据成员:
在类定义的.h文件中:
Class Data { public: Date(int value) ... }; Class Student { public: Student(int value) ... private: Date m_dd; }; |
在类Student定义的.cpp文件中:
Student::Student(int value):m_dd(value) { ... } |
注:类data的常对象,由于是常量,所以只能在类student的构造函数的成员初始化列表里进行初始化,在构造函数函数体内部或者在m_bb的定义处进行初始化都是错误的
虚继承:
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。