(根据《C++程序设计》(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明)

由于基类的构造函数和析构函数是不能被继承的,所以在声明派生类时,需要我们自己定义派生类的构造函数和析构函数。

1  派生类的构造函数

在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还要考虑基类的数据成员的初始化。也就是说,

希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。

1.1  简单派生类的构造函数

简单的派生类只有一个基类,只有一级派生,且在派生类的数据成员中不包含基类的对象。通过具体的例子定义简单派生类的构造函数:

   1: class Student            //声明基类Student
   2: {
   3: public:
   4:     Student( int n , string nam , char s )        //定义基类的析构函数
   5:     {
   6:         num = n ;
   7:         name = nam ;
   8:         sex = s ;
   9:     }
  10:     ~Student(){}        //基类的析构函数
  11: protected:
  12:     int num ;
  13:     string name ;
  14:     char sex ;
  15: };
  16:  
  17: class Student1 : public Student            //声明公用派生类Student1
  18: {
  19: public:
  20:     Student1( int n , string nam , char s , int a , string ad ) : Student( n , nam ,s )
  21:     //定义派生类的构造函数
  22:      //在函数体内只对派生类新增的数据成员初始化
  23:     {
  24:         age = a ; 
  25:         addr = ad ;
  26:     }
  27:     ~Student1(){}        //派生类的析构函数
  28: private:
  29:     int age ;
  30:     string addr ; 
  31: };

请大家注意派生类构造函数首行的写法:

Student1( int n , string nam , char s , int a , string ad ) : Student( n , nam ,s )    
可以看出派生类构造函数一般形式为:
 
                      派生类构造函数名(总参数表) : 基类构造函数名(参数表)
                      {派生类中新增的数据成员初始化语句}
 
冒号":"前面的部分是派生类构造函数的主干,和普通类的构造函数相同,但是它的总参数表中包括基类构造函数所需的参数和对派生类新增的数据成员初始化所需的参数。冒号":"后面部分是要调用的基类构造函数及其参数。注意基类构造函数后面括号内的参数列表只有参数名而不包括参数类型(如 n , nam ,s ) ,因为这里不是定义基类的构造函数,而是调用基类的构造函数,因此,这些参数是实参而不是形参。若在main函数中定义stud1对象时指定了5个实参。它们按顺序传递给派生类构造函数Student1的形参( n , nam , s , a, d ) 。然后派生类的构造函数将前面3个( n , nam , s )传递给基类构造函数的形参。这样就同时完成了对从基类继承过来的数据成员和新增数据的初始化。
 
   
  当然也可以将派生类的构造函数在类的外面定义,在类体中只写该函数的声明即可,这是声明不包括“基类构造函数名(参数表)”部分:
Student1( int n , string nam , char s , int a , string ad ) ; 
在类的外面定义构造函数:
Student1 :: Student1 (int n , string nam , char s , int a , string ad ): Student( n, nam ,s )
{
age = a ;
addr = ad ;
}

1.2   有子对象的派生类的构造函数

类中的数据成员除了标准型(int, char等)或系统提供的类型(如string)外,类中的数据成员还可以包括类的对象,例如以上例为基础,可以在声明一个类时包含这样的数据成员:

Student s1 ;         //Student是已声明的类名,s1是Student类的对象
   
其中s1是类中的内嵌对象,称为子对象,即对象中的对象。通过具体的例子来说明问题,以上例为基础,除了在派生类中增加数据成员age,addr外,还可以增加基类Student的对象monitor,也就是派生类中的子对象。程序在以上基础上略改如下:
 
   1: class Student            //声明基类,为简化,在上例的基础上去掉成员变量 char sex
   2: {
   3: public:
   4:     Student ( int n , string nam )
   5:     {
   6:         ...
   7:     }
   8:     ...
   9: };
  10:  
  11: class Student1 : public Student    //声明公用的派生类
  12: {
  13: public:
  14:     Student1 ( int n , string nam , int n1 , string nam1 , int a , string ad )
  15:         : Student ( n , nam ) , monitor ( n1 , nam1 ) //派生类的构造函数
  16:     {
  17:         age = a ;
  18:         addr = ad ;
  19:     }
  20: private:
  21:     Student monitor ;            //派生类中含有基类的对象
  22:     int age ; 
  23:     string addr ;
  24: };
 
赋值过程如下:
 
 
由程序可以看出,子对象的初始化是在建立派生类时通过调用派生类的构造函数实现的。
归纳一下,定义派生类构造函数的一般形式为:
 
                            派生类构造函数名(总参数表):基类构造函数名(参数表),子对象名(参数表)
                                  {派生类中新增数据成员初始化语句}
 
     派生类构造函数的任务包括3个部分:
    (1)对基类数据成员初始化;
    (2)对子对象的数据成员进行初始化;
    (3)对派生类数据成员初始化。
     执行派生类构造函数的顺序是:
    (1)调用基类的构造函数,对基类数据成员初始化;
    (2)调用子对象构造函数,对子对象数据成员初始化;
    (3)再执行派生类构造函数本身,对派生数据成员初始化。
    注意:基类构造函数和子对象的次序可以是任意的,编译系统是根据相同的参数名来确定传递关系的。

1.3  多层派生时的构造函数

一个类不仅可以派生出一个派生类,派生类还可以继续派生,形成派生的层次结构。通过例子来说明:

   1: class Student                                  //声明基类
   2: {
   3: public:
   4:     Student ( int n , string nam )             //基类的构造函数
   5:         {
   6:             num = n ;
   7:             name = nam ;
   8:         }
   9: protected:                                     //基类的保护数据成员
  10:     int num ;
  11:     string name ;
  12: };
  13:  
  14: class Student1 : public Student                //声明公用派生类Student1
  15: {
  16:     Student1 ( int n , string nam , int a ): Student( n ,nam )     //派生类的构造函数
  17:     {
  18:     age = a ;                                  //函数体内只对新增的数据成员初始化
  19:     }
  20: private:                                      //派生类的私有数据成员
  21:     int age ;
  22: };
  23:  
  24: Student2 : public Student1                   //声明间接公用派生类Student2
  25: {
  26: public:
  27:     Student2(int n ,string nam , int a , int s ): Student1( n , nam ,a )     //间接派生类的构造函数
  28:     {
  29:         score = s ;                           //函数体内只对新增的数据成员初始化
  30:     }
  31: private:
  32:     int score ;                             //间接派生类新增数据成员
  33: };
注意派生类Student2构造函数的首部:
Student2( int n , string nam , int a , int s ):Student1( n , nam , a )
而不要写成:
Student2( int n , string nam , int a , int s ) : Student( n , nam ),Student1(n , nam , a )
 
不要列出每一层派生类的构造函数,只需列出其直接基类的构造函数。在声明Student2对象时,调用Student2的构造函数;再执行Student2构造函数时,先调用Student1的构造函数;再执行Student1的构造函数时,先调用基类的构造函数。

1.4  派生类构造函数的特殊形式

有两种特殊形式:

(1)当不需要对派生类新增的数据成员进行任何的初始化操作时,派生类构造函数的函数体可以为空。此时派生类构造函数的作用只是为了将参数传递给基类的构造函数和子对象,并在执行派生类的构造函数时调用基类的构造函数和子对象的构造函数。

(2)如果在基类中没有定义构造函数,或是定义了没有参数的构造函数,那么,在定义派生类的构造函数时可以不写基类的构造函数。在调用派生类的构造函数时,系统会自动首先调用基类的默认构造函数。

2    派生类的析构函数

析构函数的作用是在对象撤销之前,进行必要的清理工作。同样的,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的虚构函数,用来对派生类中所增加的数据成员进行清理工作。基类的清理工作仍然由基类的析构函数负责,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。

调用的顺序与构造函数相反:先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理。

C++学习之路—继承与派生(二):派生类的构造函数与析构函数的更多相关文章

  1. C++学习之路—继承与派生(一):基本概念与基类成员的访问属性

    (本文根据<c++程序设计>(谭浩强)总结而成,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao,转载请注明) 1   基本思想与概念 在传统的程序设计 ...

  2. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

  3. C++学习笔记(6)----基类和派生类的构造函数和析构函数的执行顺序

    基类和派生类:构造函数和析构函数的执行顺序 在Visual Studio中,新建控制台工程,构造类如下: #include<iostream> using namespace std; c ...

  4. C++:派生类的构造函数和析构函数的调用顺序

    一.派生类 在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步: • 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员. • 改造基类函数:在 ...

  5. C++:派生类的构造函数和析构函数

    4.2 派生类的构造函数和析构函数4.2.1 派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数:当撤销派生类对象时,则先执行派生类 ...

  6. cc28c_demo.cpp,派生类的构造函数和析构函数-代码示范3

    cc28c_demo.cpp,派生类的构造函数和析构函数-代码示范3 //派生类的构造函数和析构函数//派生类的构造函数(执行步骤)//--执行基类的构造函数//--执行成员对象的构造函数//--执行 ...

  7. C++学习之路—继承与派生(四)拓展与总结

    (根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 1    拓展部分 本节主要由两部分内容组成,分 ...

  8. C++学习之路—继承与派生(三):多重继承与虚基类

    (根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 多重继承是指一个派生类有两个或多个基类.例如,有 ...

  9. 【温故知新】——BABYLON.js学习之路·前辈经验(二)

    前言:在上一篇随笔BABYLON.js学习之路·前辈经验(一)中回顾了组内同事们长时间在Babylon开发实践中的总结出的学习之路和经验,这一篇主要对开发中常见的一些功能点做一个梳理,这里只作为温故知 ...

随机推荐

  1. python读取EXCLE文件数据

    python读取EXCEL,利用 Google 搜索 Python Excel,点击第一条结果http://www.python-excel.org/ ,能够跨平台处理 Excel. 按照文档一步步去 ...

  2. bash有空格的文件名

    http://www.keakon.net/2011/10/20/bash%E4%B8%8B%E5%A4%84%E7%90%86%E5%8C%85%E5%90%AB%E7%A9%BA%E6%A0%BC ...

  3. Dispatcher & Redirect

    首先理解一下二者的含义:Dispatcher请求转发,直接把客户端的请求在服务器处理以后跳转到下一个页面或者是处理类.此时的地址栏上的URL是不会变化的. Redirect是重定向.客户端的请求到达服 ...

  4. Chapter 12 外观模式

    外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个模式使得这一子系统更加容易使用. 外观模式三个阶段: 首先,在设计初期阶段,应该要有意识的将不同的两个层分离. 其次,在 ...

  5. Socket编程模式

    Socket编程模式 本文主要分析了几种Socket编程的模式.主要包括基本的阻塞Socket.非阻塞Socket.I/O多路复用.其中,阻塞和非阻塞是相对于套接字来说的,而其他的模式本质上来说是基于 ...

  6. hibernate对应的annocation版本

    Hibernate Compatibility Matrix Package Version Core Annotations EntityManager Validator Search Shard ...

  7. Clojure学习02:语法

    相比我们传统的 c ,java ,python ,javascript等,Clojure的语法比较特别,初一看,还可能会有些不适应. 本文来介绍下Clojure的语法特点. 一.表达式 所有的Cloj ...

  8. CSS基础3——使用CSS格式化元素内容的字体

    1.CSS属性单位: (1)长度单位:包含绝对长度单位和相对长度单位 绝对长度单位包含:cm.mm.in.pt.pc等. 绝对长度单位最好用于打印输出设备.在仅作为频幕显示时.绝对长度值并没有什么意义 ...

  9. 演练5-4:Contoso大学校园管理系统4

    在之前的教程中,我们已经完成了学校的数据模型.现在我们将读取和显示相关数据,请理解EF加载导航属性的方式. 一.Lazy.Eager.Explicit数据加载 使用EF为实体中的导航属性加载相关数据, ...

  10. WPF4多点触摸事件

    原文 WPF4多点触摸事件 UIElement在WPF4下添加了很多支持多点触摸的事件,通过它们可以在硬件支持的情况下处理多点触摸,以下通过代码来说明通过处理这些事件,我们可以做些什么: 一.触摸相关 ...