C++中的多态和继承
1:关于三种继承方式
public继承: 若派生类public继承自基类,则基类的public成员和protected成员也是派生类的public和protected成员,
这是可在派生类的成员函数中显式的访问他们。但基类的private成员派生类不能访问(即既不能通过派生类的对象 , ,指针,引用访问也不能在派生类的成员函数或派生类的友元中访问)。
protected继承: 这是基类的public成员和protected成员都是派生类的protected成员,可在派生类的成员函数中访问他们,
但是像public继承一样,派生类不能访问基类的private成员
private继承: 一般我们很少用这种继承方式,因为继承过后派生类不能访问到基类的成员
2:关于虚函数
关于动态绑定的概念前文有介绍,现在说说虚函数。
我们使用虚函数很大程度上是因为要用到动态绑定技术。我们对派生类中的虚函数的要求是:虚函数的定义必须和基类中虚函数的定义一样(返回值可不同),假如说在派生类中定义了不一样的虚函数,那么这时发生的就是函数的覆盖:即派生类中的同名函数覆盖了从基类继承下来的虚函数,导致我们无法使用派生类继承下来的虚函数,举个例子说明下:
class base{
public:
virtual void menfcn(){}
};
class dase:public base
{
public:
void memfcn(int n){}
}
dase d;
base *p=&d;
p->memfcn();
这是调用的就是基类的memfcn()实现(这地方牵扯到动态绑定确定函数的过程,下面再讲)
总之,我们对虚函数的态度就是,在基类中定义的虚函数,我们在派生类中最好是原样照写( {}里的内容是可变的 ),同时,若想定义函数的不同版本,就会用到函数的重载
3:关于动态绑定确定函数的过程
这个函数的确定秉承了一个从上到下的过程: 在动态绑定后调用函数时,首先在基类中查找该函数的实现,若没有匹配的函数,则编译报错,否则,在第一派生类中寻找该匹配函数,若找到,则接着在第二派生函数中查找匹配函数;若没有找到,则东带匹配过程就此结束,最终虽然是用第X派生类去初始化基类的指针(引用),但调用的却是基类的函数实现
总之,动态绑定的查找过过程就是从上到下查找最后一个匹配的函数,并调用它。
基类->第一派生类->第二派生类->.......->第n派生类
4:构造函数和复制构造函数
在定义派生类的构造函数时,应显示的调用基类的构造函数。在合成的派生类构造函数中,会引用的调用基类的默认构造函数。
关于派生类自身成员和基类成员初始化顺序是:派生类构造函数总是默认的先运行基类的构造函数在去初始化派生类自己的成员
还有,一个类只能初始化自己的直接基类
关于派生类复制构造函数:假如说我们定义了派生类的复制构造函数的话,那么我们应该定义基类的复制构造函数,并在派生类复制构造函数的饿初始化列表中显示的调用基类的复制构造函数。假如说我们省略了调用基类的复制构造函数,那么派生类的复制构造函数就会隐式的执行基类的默认构造函数(如果基类的默认构造函数存在的话,如果不存在则编译错误)
另外,如果派生类定义了自己的复制操作符函数的话,我们也应该定义基类的这个函数,并在派生类函数中显示的调用它
5: 虚析构函数
我们为什么要把析构函数定义为虚函数,看下面的例子
class base{
public:
base(){}
~base(){}
protected:
int num;
};
class dase:public base{
public:
dase():base(){}
~dase(){}
protected:
int number;
};
dase d;
base *p=&d;
delete p;
当我们执行delete p时,根据静态绑定规则,这是我们调用的是基类的析构函数,但是我们的这个指针p指向的却是dase对象,我们的本意是回收在堆上分配的d这个dase对象,但是我们实际上只是回收了对象d的从基类继承下来的部分
假如说把析构函数座位虚函数的话,这是delete p首先会这姓dase::~dase 再执行base::~base,即这时是从下往上的调用析构函数(这是因为每个类的析构函数总是只负责清除自己的成员,所以派生类对象析构时,会自动的调用基类的析构函数)
在有继承关系的类中,我们总应该把析构函数设置为虚函数。