基类的构造函数不能被继承,在声明派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数来完成。所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要考虑基类的成员变量,要让它们都被初始化。

解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。

下面的例子展示了如何在派生类的构造函数中调用基类的构造函数。

#include<iostream>
using namespace std;
//基类
class People{
protected:
char *name;
int age;
public:
People(char*, int);
};
People::People(char *name, int age): name(name), age(age){}
//派生类
class Student: public People{
private:
float score;
public:
Student(char*, int, float);
void display();
};
//调用了基类的构造函数
Student::Student(char *name, int age, float score): People(name, age){
this->score = score;
}
void Student::display(){
cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
int main(){
Student stu("小明", , 90.5);
stu.display();
return ;
}

请注意代码第23行:

Student::Student(char *name, int age, float score): People(name, age)

这是派生类 Student 的构造函数的写法。冒号前面是派生类构造函数的头部,这和我们以前介绍的构造函数的形式一样,但它的形参列表包括了初始化基类和派生类的成员变量所需的数据;冒号后面是对基类构造函数的调用,这和普通构造函数的参数初始化表非常类似。

实际上,你可以将对基类构造函数的调用和参数初始化表放在一起,如下所示:

Student::Student(char *name, int age, float score): People(name, age), score(score){}

基类构造函数和初始化表用逗号隔开。

需要注意的是:冒号后面是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数总参数表中的参数,还可以是局部变量、常量等。如下所示:

Student::Student(char *name, int age, float score): People("李磊", )

基类构造函数调用规则

事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。也就是说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。

请看下面的例子:

#include<iostream>
using namespace std;
//基类
class People{
protected:
char *name;
int age;
public:
People();
People(char*, int);
};
People::People(){
this->name = "xxx";
this->age = ;
}
People::People(char *name, int age): name(name), age(age){}
//派生类
class Student: public People{
private:
float score;
public:
Student();
Student(char*, int, float);
void display();
};
Student::Student(){
this->score = 0.0;
}
Student::Student(char *name, int age, float score): People(name, age){
this->score = score;
}
void Student::display(){
cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
int main(){
Student stu1;
stu1.display();
Student stu2("小明", , 90.5);
stu2.display();
return ;
}

创建对象 stu1 时,执行派生类的构造函数 Student::Student(),它并没有指明要调用基类的哪一个构造函数,从运行结果可以很明显地看出来,系统默认调用了不带参数的构造函数,也就是 People::People()。

创建对象 stu2 时,执行派生类的构造函数 Student::Student(char *name, int age, float score),它指明了基类的构造函数。

在第31行代码中,如果将 People(name, age) 去掉,也会调用默认构造函数,stu2.display() 的输出结果将变为:
xxx的年龄是0,成绩是90.5

如果将基类 People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象 stu1 时没有调用基类构造函数。

总结:如果基类有默认构造函数,那么在派生类构造函数中可以不指明,系统会默认调用;如果没有,那么必须要指明,否则系统不知道如何调用基类的构造函数。

构造函数的调用顺序

为了搞清这个问题,我们不妨先来看一个例子:

#include<iostream>
using namespace std;
//基类
class People{
protected:
char *name;
int age;
public:
People();
People(char*, int);
};
People::People(): name("xxx"), age(){
cout<<"PeoPle::People()"<<endl;
}
People::People(char *name, int age): name(name), age(age){
cout<<"PeoPle::People(char *, int)"<<endl;
}
//派生类
class Student: public People{
private:
float score;
public:
Student();
Student(char*, int, float);
};
Student::Student(): score(0.0){
cout<<"Student::Student()"<<endl;
}
Student::Student(char *name, int age, float score): People(name, age), score(score){
cout<<"Student::Student(char*, int, float)"<<endl;
}
int main(){
Student stu1;
cout<<"--------------------"<<endl;
Student stu2("小明", , 90.5);
return ;
}

从运行结果可以清楚地看到,当创建派生类对象时,先调用基类构造函数,再调用派生类构造函数。如果继承关系有好几层的话,例如:

A --> B --> C

那么则创建C类对象时,构造函数的执行顺序为:

A类构造函数 --> B类构造函数 --> C类构造函数

构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。

C++学习17派生类的构造函数的更多相关文章

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

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

  2. 【C++学习之路】派生类的构造函数(三)

    三.多层继承的派生类 1.多层继承的派生类只需在构造函数的初始化列表中写出直接基类的构造函数即可 class student { public: student(int n, string nam) ...

  3. 【C++学习之路】派生类的构造函数(一)

    一.简单派生类的构造函数 1.所谓简单派生类,就是指派生类中不包含基类的内嵌对象的派生类. 2.一般来说,这样的派生类的构造函数的形式是: student( int i, string nam, in ...

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

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

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

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

  6. 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符

    学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...

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

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

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

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

  9. C++有子对象的派生类的构造函数

    转载:https://blog.csdn.net/qq1169091731/article/details/50934588?utm_source=blogxgwz6 类的数据成员不但可以是标准型(如 ...

随机推荐

  1. C#中的预处理指令

    C#中的预处理指令 作为预处理中的一对:#region name ,#endregion可能是大家使用得最多的,我也常用它来进行代码分块,在一个比较长的cs文件中,这么做确实是一件可以让你使代码更清晰 ...

  2. 虚拟化之esxi命令行管理之二

    /vmfs # ls -l lrwxrwxrwx 1 root root 4 Mar 23 2013 devices -> /dev drwxr-xr-x 1 root root 512 Sep ...

  3. Postman接口测试初探

    Postman接口测试 有两种安装方式: 1)Chrome插件(https://www.getpostman.com/).安装完成后,它会在chrome的应用中,如下图 2)通过下载Native ap ...

  4. VS 2010 编译安装 boost 库 -(和 jsoncpp 库共存)

    boost库的简单应用很容易,网上有很多资料,但是,如果要json 和 boost 一起使用就会出现这样那样的问题, 有时候提示找不到 “libboost_coroutine-vc100-mt-sgd ...

  5. 【extjs】 ext5 Ext.grid.Panel 分页,搜索

    带有分页,搜索的grid. <%@page language="java" contentType="text/html; charset=UTF-8" ...

  6. ajax语法

    js语言功能比较强大,但不能访问数据库 ajax来补充这一缺陷 特点:输出不用刷新页面,条件查询数据显示页面上一般不用它,因为需要造很多表格不如用嵌入php代码方式简单 ajax语法: $.ajax( ...

  7. [原]Fedora 20安装记录

    Fedora是我最喜欢的Linux版本,很长时间以来我都在安装使用.近一年多以来一直在搞一个C#相关的开发,很久都没有接触Fedora了,我上一次使用的版本还是Fedora 17.本以为作为一个“老” ...

  8. IOS的沙盒机制

    ios的沙盒(bandbox)机制:一种安全体系,ios应用程序只能对自己创建的应用程序进行读取文件,这个独立.封闭.安全的空间,就我们说的沙盒.它里面一般存放着你的程序需要的文件,数据持久化的一些文 ...

  9. HackerRank "Playing with numbers"

    This is 'Difficult' - I worked out it within 45mins, and unlocked HackerRank Algorithm Level 80 yeah ...

  10. HackerRank "No Prefix Set"

    Typical Trie usage. But please note that it could be any order of input strings. #include <algori ...