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

//例4.5 派生类的构造函数和析构函的执行顺序

#include<iostream>
using namespace std;
class Base{ //声明基类Base
public:
Base()
{
cout<<"Constructor Base Class.."<<endl;
}
~Base()
{
cout<<"Destructor Base Class.."<<endl;
}
};
class Derived: public Base{ //声明基类Base的公有派生类Derived
public:
Derived()
{
cout<<"Constructor Derived Class.."<<endl;
}
~Derived()
{
cout<<"Destructor Derived Class.."<<endl;
}
};
int main()
{
Derived obj;
return ;
}
/*
说明:从程序运行的结果可以看出:构造函数的调用严格按照先调用基类的构造函数,后调用派生类的
构造函数的顺序执行。析构函数的调用顺序与构造函数的调用顺序正好相反,先调用派生类的析
构函数,再调用基类的析构函数。
*/

4.2.2 派生类构造函数和析构函数的构造规则

1. 简单的派生类的构造函数

当基类的构造函数没有参数,或没有显式定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数。例4.5的程序就是由于基类的构造函数没有参数,所以派生类没有向基类传递参数。

派生类不能继承基类中构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。

在C++中,派生类构造函数的一般格式为:

派生类名(参数总表):基类名(参数表)

派生类新增数据成员的初始化语句

其中,基类构造函数的参数,通常来源于派生类构造函数的参数总表,也可以用常数值。

//例4.6 当基类含有带参数的构造函数,派生类构造函数的构造方法。

#include<iostream>
#include<string>
using namespace std;
class Student{ //声明基类Student
public:
Student(int number1,string name1,float score1) //基类的构造函数
{
number = number1;
name = name1;
score = score1;
}
void print()
{
cout<<"number:"<<number<<endl;
cout<<"name:"<<name<<endl;
cout<<"score:"<<score<<endl;
}
protected:
int number;
string name;
float score;
};
class UStudent:public Student{ //基类Student的公有派生类UStudent
public:
UStudent(int number1,string name1,float score1,string major1)
:Student(,"张三",/*number1,name1,score1*/)
{ //定义派生类的构造函数时,缀上要调用的基类的构造函数及其参数
major = major1; //参数可以是派生类构造函数总参数表中的参数,也可以是常量和全局变量。
}
void print1()
{
print();
cout<<"major:"<<major<<endl;
}
private:
string major;
};
int main()
{
UStudent stu(,"张志",,"信息安全");
//stu.print();
stu.print1();
return ;
}

注意事项:

请注意派生类构造函数首行的写法:
UStudent(int number1,string name1,float score1,string major1):Student(number1,name1,score1)

冒号前面的部分是派生类构造函数的主干,它和以前介绍过的构造函数的形式相同,但它的总参数表中包括基类构造函数所需要的参数和对派生类新增的数据成员初始化所需要的参数。冒号后面的部分是要调用的基类构造函数及其参数。

从上面列出的派生类UStudent构造函数首行中可以看到,派生类构造函数名(UStudent)后面的总参数表中包括参数的类型和参数名(如 int number1),而基类构造函数的参数表中只有参数名而不包括参数类型(如 number1),因为在这里不是再定义基类构造函数,而是调用基类构造函数,因此这些参数是实参而不是形参。它们可以是派生类构造函数总参数表中的参数,也可以是常量和全局变量。

说明:1、可以将派生类构造函数定义在类的外部,而在类体内只写该函数的声明。如在例4.6的派生类中可以只写构造函数的声明:
UStudent(int number1,string name1,float score1,string major1)
而在类外定义派生类的构造函数:
UStudent::UStudent(int number1,string name1,float score1,string major1):Student(number1,name1,score1)
{
          major = major1;
}
请注意:在类中声明派生类构造函数时,不包括基类构造函数及其参数表(Student(number1,name1,score1),只在
类外定义构造函数时才将它列出。

2、若基类使用默认构造函数或不带参数的构造函数,则在派生类中定义构造函数时, "基类构造函数名(参数表)"可以省略。
如在例4.5的程序中,由于基类的构造函数没有参数,所以在派生类中定义构造函数时,不要缀上":Base()",也即
不必写成:

Deirved:Base()
{
cout<<"Constructor derived class"<<endl;
}

3、当基类构造函数不带参数时,派生类不一定需要定义构造函数,然而当基类的构造函数哪怕只带有一个参数,它所有的派生类必须定义构造函数,甚至所定义的派生类构造函数的函数体可能为空,仅仅起到参数的传递作用。
例如,在下面的程序中,派生类Deruved就不使用参数n,n只是被传递给基类构造函数Base。

 class Base{
public:
Base(int n)
{
i = n;
}
void show()
{
cout<<"i="<<i<<endl;
}
private:
int i;
};
class Derived public Base{
public:
Derived(int n):Base(n) //构造函数参数表中只有一个参数,
{ } //传递给了要调用的基类构造函数Base
}; //派生类构造函数体为空

2.派生类的析构函数

在派生类中可以根据需要定义自己的析构函数,用来对派生类中的所增加的成员进行清理工作。基类的清理工作仍然有基类的析构函数负责。由于析构函数是不带参数的,在派生类中是否要定义析构函数与它所属基类的析构函数无关。在执行派生类的析构函数时,系统会自动调用基类的析构函数,对基类的对象进行清理。析构函数的调用顺序与构造函数正好相反,先执行派生类的析构函数,再执行基类的析构函数。

//例4.7 简单派生类的构造函数和析构函数的执行顺序。

#include<iostream>
using namespace std;
class A{ //声明基类A
public:
A()
{
cout<<"Constuctor class A"<<endl; //基类的构造函数
}
~A()
{
cout<<"Destuctor class A"<<endl; //基类的构造函数
}
};
class B:public A{
public:
B()
{
cout<<"Constuctor class B"<<endl; //派生类的构造函数
}
~B()
{
cout<<"Destuctor class B"<<endl; //派生类的构造函数
}
};
int main()
{
B b;
return ;
}
/*
运行结果如下: Constuctor class A
Constuctor class B
Destuctor class B
Destuctor class A
*/

3.含有对象成员(子对象)的派生类的构造函数

当派生类中含有内嵌的对象成员(也称子对象)时,其构造函数的一般格式为:

派生类(参数总表):基类名(参数表 0):对象成员1(参数表 1),......,对象成员n(参数表 n)
{
派生类新增成员的初始化语句
}

在定义派生类对象时,构造函数的执行顺序如下:
(1)、调用基类的构造函数,对基类数据成员初始化;
(2)、调用内嵌对象成员的构造函数,对内嵌对象成员的数据成员进行初始化;
(3)、调用派生类的构造函数体,对派生类数据成员进行初始化。

撤销对象时,析构函数的调用顺序与构造函数的调用顺序相反,首先执行派生类的析构函数,
再执行内嵌对象成员的析构函数,最后执行基类的析构函数。

//例4.8 含有对象成员的派生类构造函数和析构函数的执行顺序

#include<iostream>
using namespace std;
class Base{ //声明基类Base
public:
Base(int i) //基类的构造函数
{
x = i;
cout<<"Constructor base class"<<endl;
}
~Base() //基类的析构函数
{
cout<<"Destructor base class"<<endl;
}
void show()
{
cout<<"x = "<<x<<endl;
}
private:
int x;
};
class Derived :public Base{ //声明基类Base的公有派生类Derived
public:
Derived(int i):Base(i),d(i) //派生类的构造函数,缀上要调用的基类构造函数和对象成员的构造函数
{
cout<<"Constructor Derived class"<<endl;
}
~Derived()
{
cout<<"Destructor Derived class"<<endl;
}
private:
Base d;
};
int main()
{
Derived obj();
obj.show();
return ;
}
/*
运行结果是:Constructor base class
Constructor base class
Constructor Derived class
x = 5
Destructor Derived class
Destructor base class
Destructor base class */

说明:
(1)在派生类中含有多个内嵌对象成员时,调用内嵌对象成员的构造函数顺序由它们在类中声明的顺序确定。
(2)如果派生类的基类也是一个派生类,每个派生类只需负责其直接基类数据成员。依次上溯。

//例4.9 含有多个对象成员的派生类构造函数的执行顺序。

#include<iostream>
#include<string>
using namespace std;
class Student{ //声明基类Base
public:
Student(int number1,string name1,float score1)
{
number = number1;
name = name1;
score = score1;
}
void print()
{
cout<<"学号:"<<number<<endl;
cout<<"姓名:"<<name<<endl;
cout<<"成绩:"<<score<<endl;
}
protected:
int number;
string name;
float score;
};
class UStudent:public Student{ //声明基类Student的公有派生类UStudent
public:
UStudent(int number1,string name1,float score1,int number2,string name2,float score2,
int number3,string name3,float score3,string major1):Student(number1,name1,score1),
auditor2(number3,name3,score3),auditor1(number2,name2,score2)
{
major = major1;
}
void print()
{
cout<<"正式生是:"<<endl;
Student::print();
cout<<"专业是:"<<major<<endl;
}
void print_auditor1()
{
cout<<"旁听生是:"<<endl;
auditor1.print();
}
void print_auditor2()
{
cout<<"旁听生是:"<<endl;
auditor2.print();
}
private:
string major; //专业
Student auditor1; //定义对象成员1(旁听生)
Student auditor2; //定义对象成员2(旁听生)
};
int main()
{
UStudent stu(,"张志",,,"王全安",,,"李倩倩",,"信息安全");
stu.print(); stu.print_auditor1();
stu.print_auditor2();
return ;
}

C++:派生类的构造函数和析构函数的更多相关文章

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

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

  2. C++学习之路—继承与派生(二):派生类的构造函数与析构函数

    (根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 由于基类的构造函数和析构函数是不能被继承的,所以 ...

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

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

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

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

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

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

  6. c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]

    说明:文章中关于operator=实现的示例,从语法上是对的,但逻辑和习惯上都是错误的. 参见另一篇专门探究operator=的文章:<c++,operator=>http://www.c ...

  7. c++学习笔记4,派生类的构造函数与析构函数的调用顺序(一)

    測试源代码: //測试派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace ...

  8. c++派生类中构造函数和析构函数执行顺序、判断对象类型、抽象类、虚函数

    一. 代码: 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include&l ...

  9. c++ 单继承派生类的构造函数

    1.派生类的构造函数: #include <iostream> #include<string> using namespace std; class Student//声明基 ...

随机推荐

  1. DataSnap如何监控Tcp/IP客户端的连接情况

    一个实例,如果客户端是TCP/IP是短连接的情况就没有必要了. 一.GlobVar.pas单元,定义应用系统全局数据类型及变量: unit GlobVar; interface uses System ...

  2. DB 从zl.xml中导入数据库用户名及密码等!

    package com.dy.java; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  3. SQL拼接备份数据库

    在SQLserver使用脚本备份数据库的时候需要注意的问题是: 1.指向的文件名必须是有读写权限. 2.在使用批量数据库备份时候需要根据自己需求选择性备份. -- ================== ...

  4. 从零开始学ios开发(二十):Application Settings and User Defaults(下)

    在上一篇的学习中,我们知道了如何为一个App添加它的Settings设置项,在Settings设置项中我们可以添加哪些类型的控件,这些控件都是通过一个plist来进行管理的,我们只需对plist进行修 ...

  5. 深入理解ThreadLocal(一)

    Android里,在不同的线程(假设子线程已经创建了Looper)中创建Handler时,并不需要显式指定Looper,系统能自动找到该线程自己的Looper.不同线程的Looper相互独立,之所以能 ...

  6. Android bluetooth low energy (ble) writeCharacteristic delay callback

    I am implementing a application on Android using BLE Api (SDK 18), and I have a issue that the trans ...

  7. iOS xcode 8 注释快捷键

    单行注释:在方法的地方按 Command+/ 标注的功能,快捷键是Command + Option + / 需要在方法名的上面(空白)的地方按 Command + Option + /      才管 ...

  8. C# - 设置DLL的属性Embed Interop Type 设为False

    错误: Error msg: A reference was created to embedded interop assembly. because of an indirect referenc ...

  9. 初识PCA数据降维

    PCA要做的事降噪和去冗余,其本质就是对角化协方差矩阵. 一.预备知识 1.1 协方差分析 对于一般的分布,直接代入E(X)之类的就可以计算出来了,但真给你一个具体数值的分布,要计算协方差矩阵,根据这 ...

  10. 剑指offer--面试题5

    到现在为止,看过的书+代码有一定量了,并且也参加了个比赛,给自己的总体感觉:编程需要的是灵活的头脑,书里的东西只是讲个规则.思想,其实际实现可以千差万别!   潜在的规则+灵活的思维 = 程序! 在做 ...