不是所有的函数都能自动地从基类继承到派生类中的。
构造函数和析构函数是用来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么。
所以,在整个层次中的所有的构造函数和析构函数都必须被调用,也就是说,构造函数和析构函数不能被继承。
子类的构造函数会显示的调用父类的构造函数或隐式的调用父类的默认的构造函数进行父类部分的初始化。
析构函数也一样。它们都是每个类都有的东西,如果能被继承,那就没有办法初始化了。
此外,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。
构造原则如下:
1. 如果子类没有定义构造方法,则调用父类的无参数的构造方法。
2. 如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。
3. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数,则会调用父类的默认无参构造函数。
4. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。
5. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。
6. 如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式,比如:
在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会去调用父类的带参数的构造函数去构造对象。这种初始化方式,还常用来对类中的常量(const)成员进行初始化,如下面的代码所示:
再来看一段代码:
派生类构造函数一般形式为
派生类构造函数名(总参数表):基类构造函数名(参数表)
{派生类中新增数据成员初始化语句}
冒号“:”前面部分是派生类构造函数的主干,他和以前介绍过的构造函数的形式相同,但它的总参数表中包括基类构造函数所需的参数和对派生类新增的数据成员初始化所需的参数。冒号“:”后面部分是要调用的基类构造函数及其参数。
从上面列出的派生类Student1构造函数首行中可以看到,派生类构造函数名(Student1)后面括号内的参数表中包括参数的类型和参数名(如int n) ,
而基类构造函数名后面括号内的参数表列只有参数名而不包括参数类型(如n,num,s),因为在这里不是定义基类构造函数,而是调用基类构造函数,因此这些参数是实参而不是形参。
它们可以是常量、全局变量和派生类构造参数总参数表中的参数。
从上面列出的派生类Student1构造函数中可以看到:调用基类构造函数Student时给出3个参数(n,nam,s),这是和定义基类构造函数时指定的参数相匹配的。
派生类构造函数Student1有5个参数,其中前3个是用来传递给基类构造函数,后面两个(a和ad)是用来对派生类所增加的数据成员初始化的。
2.派生类的析构函数
析构函数的作用是在对象撤销之前,进行必要的清理工作。
在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来地派生类中所增加的成员进行清理工作。基类的清理工作任然由基类的析构函数负责。
调用的顺序与构造函数正好相反:先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理。