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. Python脚本控制的WebDriver 常用操作 <二十六> 上传文件

    测试用例场景 上传文件的方法是找到上传文件的对象,通常是的对象.然后直接往这个对象send_keys,传入需要上传文件的正确路径.绝对路径和相对路径都可以,但是上传的文件必须存在,否则会报错. Pyt ...

  2. Mysql数据库中的计数器表实时更新

    如果某个应用中存在计数器,例如网站的总访问量.用户的粉丝数.文件下载数等等.如果相关应用在Mysql数据库的表中保存计数器,在更新计数器的时候可能会碰到并发问题.例如在web应用中,记录网站的点击次数 ...

  3. 在Web API中使用Swagger-UI开源组件(一个深坑的解决)

    介绍: Swagger-Ui是一个非常棒的Web API说明帮助页,具体详情可自行Google和百度. 官网:http://swagger.io/    GitHub地址:https://github ...

  4. iOS 进阶 第六天(0402)

    0402 通知和代理的区别 代理是一对一的,只能是调用实现了协议里的方法,对象作为实现了该方法才能执行方法 通知是多对多,它是通过通知中心分发 通知要及时移除,如果不及时移除可能会收到多次通知,就好像 ...

  5. iOS常见问题(1)

    一.storyboard连线问题 产生原因:将与storyboard关联的属性删除了,但是storyboard中还保持之前所关联的属性. 解决: 1.点击view controller 2.点击这排最 ...

  6. js SVG

    Snap.svg Paths.js http://www.sitepoint.com/creating-animated-valentines-day-card-snap-svg/

  7. Ubuntu 下为 Idea 创建启动图标.

    默认情况下,ubuntu将自动安装的软件快捷方式保存在/usr/share/applications目录下,如果我们要创建桌面快捷方式,需要在该目录下创建一个名为“Idea.desktop”的文件.通 ...

  8. Careercup - Google面试题 - 4716965625069568

    2014-05-06 00:17 题目链接 原题: Given a -D matrix represents the room, obstacle and guard like the followi ...

  9. Careercup - Facebook面试题 - 6299074475065344

    2014-05-01 01:00 题目链接 原题: Given a matrix with 's. What is the maximum area of the rectangle. In . Ho ...

  10. JS Web打印,实现预览新样式

    问题描述:     JS实现Web打印,要求打印前一种样式,打印预览时新样式 问题解决:         (1)设置打印时的css样式,设置打印前的css样式 注:         以上为print. ...