C++的基类和派生类构造函数

基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。

在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。

这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数。

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

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

运行结果为:小明的年龄是16,成绩是90.5。

请注意第 23 行代码:

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

People(name, age)就是调用基类的构造函数,并将 name 和 age 作为实参传递给它,m_score(score)是派生类的参数初始化表,它们之间以逗号,隔开。也可以将基类构造函数的调用放在参数初始化表后面:

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

但是不管它们的顺序如何,派生类构造函数总是先调用基类构造函数再执行其他代码(包括参数初始化表以及函数体中的代码),总体上看和下面的形式类似:

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

当然这段代码只是为了方便大家理解,实际上这样写是错误的,因为基类构造函数不会被继承,不能当做普通的成员函数来调用。换句话说,只能将基类构造函数的调用放在函数头部,不能放在函数体中。另外,函数头部是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数参数列表中的参数,还可以是局部变量、常量等,例如

Student::Student(char *name, int age, float score): People("小明", 16), m_score(score){ }

构造函数的调用顺序

从上面的分析中可以看出,基类构造函数总是被优先调用,这说明创建派生类对象时,会先调用基类构造函数,再调用派生类构造函数,如果继承关系有好几层的话,例如:

A --> B --> C

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

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

构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。还有一点要注意,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。

以上面的 A、B、C 类为例,C 是最终的派生类,B 就是 C 的直接基类,A 就是 C 的间接基类。

C++ 这样规定是有道理的,因为我们在 C 中调用了 B 的构造函数,B 又调用了 A 的构造函数,相当于 C 间接地(或者说隐式地)调用了 A 的构造函数,如果再在 C 中显式地调用 A 的构造函数,那么 A 的构造函数就被调用了两次,相应地,初始化工作也做了两次,这不仅是多余的,还会浪费CPU时间以及内存,毫无益处,所以 C++ 禁止在 C 中显式地调用 A 的构造函数。 

基类构造函数调用规则

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

    #include <iostream>using namespace std;//基类Peopleclass People{public:People();  //基类默认构造函数People(char *name, int age);protected:char *m_name;int m_age;};People::People(): m_name("xxx"), m_age(0){ }People::People(char *name, int age): m_name(name), m_age(age){}//派生类Studentclass Student: public People{public:Student();Student(char*, int, float);public:void display();private:float m_score;};Student::Student(): m_score(0.0){ }  //派生类默认构造函数Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }void Student::display(){cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。"<<endl;}int main(){Student stu1;stu1.display();Student stu2("小明", 16, 90.5);stu2.display();return 0;}

运行结果:xxx的年龄是0,成绩是0。

小明的年龄是16,成绩是90.5。

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

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

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

如果将基类 People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象 stu1 时需要调用 People 类的默认构造函数, 而 People 类中已经显式定义了构造函数,编译器不会再生成默认的构造函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/63101.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rate-based 拥塞控制吞吐测量

要点&#xff1a;一个方法无法精确刻画链路画像&#xff0c;就用多种方法组合刻画&#xff0c;设计一个 “自定义平均”。 当前 Linux kernel TCP 实现的 TCP delivery rate 测量机制(BBR 有使用到)如下图&#xff1a; 简略后可展示为下图&#xff1a; 详见 net/ipv4/tcp_…

5G NR:RACH流程 -- Msg1发送时RA-RNTI的计算及功率控制

前言 如果阅读了这两篇博文《如何产生PRACH preamble》和《如何选择合适的时频资源发送preamble》&#xff0c;那么对msg1有了基本了解&#xff0c;但是真到了Msg1发送的时候&#xff0c;该怎么处理呢&#xff0c;这里涉及到两个问题&#xff1a; 问题1&#xff1a;发…

数据结构:排序解析

文章目录 前言一、常见排序算法的实现1.插入排序1.直接插入排序2.希尔排序 2.交换排序1.冒泡排序2.快速排序1.hoare版2.挖坑版3.前后指针版4.改进版5.非递归版 3.选择排序1.直接选择排序2.堆排序 4.归并排序1.归并排序递归实现2.归并排序非递归实现 5.计数排序 二、排序算法复杂…

训练简单的线性模型,预测电影评分和票房收入

文章目录 训练简单的线性模型&#xff0c;预测电影评分和票房收入数据集加载数据可视化数据分割数据集训练模型可视化结果应用模型保存和加载模型 训练简单的线性模型&#xff0c;预测电影评分和票房收入 这是一个简单的线性模型&#xff0c;用于预测电影的评分和票房收入。不…

idea全局搜索失效,Ctrl+shift+F快捷键不起作用

方法1&#xff1a;是否与搜狗等输入法软件存在快捷键冲突&#xff0c;当然也可能是你新下载的什么软件导致的快捷键冲突导致IDEA全局搜索失效。比如下图&#xff1a; 可以改掉输入法的快捷键或者直接关闭输入法的快捷键&#xff0c;这样idea的全局搜索功能就恢复了。 方法2&…

1.9 动态解密ShellCode反弹

动态解密执行技术可以对抗杀软的磁盘特征查杀。其原理是将程序代码段中的代码进行加密&#xff0c;然后将加密后的代码回写到原始位置。当程序运行时&#xff0c;将动态解密加密代码&#xff0c;并将解密后的代码回写到原始位置&#xff0c;从而实现内存加载。这种技术可以有效…

【Cookie和Session的那些事儿】

&#x1f320;作者&#xff1a;TheMythWS. &#x1f386;专栏&#xff1a;《集合与数据结构》 &#x1f387;座右铭&#xff1a;不走心的努力都是在敷衍自己&#xff0c;让自己所做的选择&#xff0c;熠熠发光。 目录 认识Cookie和Session Cookie Cookie对象的特点 Cookie对…

Unity 之 参数类型之值类型参数的用法

文章目录 基本数据类型结构体结构体的进一步补充 总结&#xff1a; 当谈论值类型参数时&#xff0c;我们可以从基本数据类型和结构体两个方面详细解释。值类型参数指的是以值的形式传递给函数或方法的数据&#xff0c;而不是引用。 基本数据类型 基本数据类型的值类型参数&…

数据库介绍

一.什么是数据库&#xff1f; 通俗的来讲数据库就是用来存放数据的地方&#xff0c;可以理解为和冰箱一样 官方&#xff1a; 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数据的仓库。 每个数据库都有一个或多个不同的 API 用于创建&#xff0c;…

说说Flink双流join

分析&回答 Flink双流JOIN主要分为两大类 一类是基于原生State的Connect算子操作另一类是基于窗口的JOIN操作。其中基于窗口的JOIN可细分为window join和interval join两种。 基于原生State的Connect算子操作 实现原理&#xff1a;底层原理依赖Flink的State状态存储&…

11.添加侧边栏,并导入数据

修改CommonAside的代码&#xff1a; <template><div><el-menu default-active"1-4-1" class"el-menu-vertical-demo" open"handleOpen" close"handleClose":collapse"isCollapse"><!--<el-menu-it…

UE5打完包后,启动程序不能全屏

最近看到ue5的打包程序后不能默认自动全屏&#xff0c;效果如下&#xff0c;发现并不是全屏的&#xff0c;而且就算点击放大也不是全屏 解决办法&#xff1a;设置如下之后在打包就可以了 但是会一直打印错误的日志&#xff0c;不过这个不影响使用

芯片开发之难如何破解?龙智诚邀您前往DR IP-SoC China 2023 Day

2023年9月6日&#xff08;周三&#xff09;&#xff0c;龙智即将亮相D&R IP-SoC China 2023 Day&#xff0c;呈现集成了Perforce与Atlassian产品的芯片开发解决方案&#xff0c;助力企业更好、更快地进行芯片开发。 龙智资深顾问、技术支持部门负责人李培将带来主题演讲—…

(数字图像处理MATLAB+Python)第十一章图像描述与分析-第三、四节:几何表述和形状描述

文章目录 一&#xff1a;几何描述&#xff08;1&#xff09;像素间几何关系A&#xff1a;邻接与连通B&#xff1a;距离 &#xff08;2&#xff09;像素间几何特征A&#xff1a;位置B&#xff1a;方向C&#xff1a;尺寸 &#xff08;3&#xff09;程序 二&#xff1a;形状描述&a…

yolov5自定义模型训练三

经过11个小时cpu训练完如下 在runs/train/expx里存放训练的结果&#xff0c; 测试是否可以检测ok 网上找的这张识别效果不是很好&#xff0c;通过加大训练次数和数据集的话精度可以提升。 训练后的权重也可以用视频源来识别&#xff0c; python detect.py --source 0 # webca…

Fiddler Response私人订制

在客户端接口的测试中&#xff0c;我们经常会需要模拟各种返回状态或者特定的返回值&#xff0c;常见的是用Fiddler模拟各种请求返回值场景&#xff0c;如重定向AutoResponder、请求拦截修改再下发等等。小编在近期的测试中遇到的一些特殊的请求返回模拟的测试场景&#xff0c;…

docker作业

目录 1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 1.1启动镜像 1.2启动cloud镜像 1.3浏览器访问 ​编辑 2、安装搭建私有仓库 Harbor 2.1下载docker-compose 2.2 磁盘挂载&#xff0c;保存harbor 2.3 修改配置文件 2.4安装 2.5浏览器访问 2.6 新…

MybatisPlus(2)

前言&#x1f36d; ❤️❤️❤️SSM专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring Spring MVC MyBatis_冷兮雪的博客-CSDN博客 上篇我们简单介绍了MybatisPlus的方便之处&#xff0c;这篇来深入了解Myb…

代码随想录算法训练营第二十四天|理论基础 77. 组合

理论基础 其实在讲解二叉树的时候&#xff0c;就给大家介绍过回溯&#xff0c;这次正式开启回溯算法&#xff0c;大家可以先看视频&#xff0c;对回溯算法有一个整体的了解。 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法&#xff08;理论篇…

Maven之高版本的 lombok 和 tomcat 7 插件冲突问题

高版本的 lombok 和 tomcat 7 插件冲突问题 在开发期间&#xff0c;当我们使用 tomcat7-maven-plugin 来作为运行环境运行我们项目使&#xff0c;如果我们项目中使用了 1.16.20 及以上版本的 lombok 包&#xff0c;项目启动时会报错&#xff1a; for annotations org.apache.…