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.rhkb.cn/news/114408.html

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

相关文章

C#,数值计算——Midinf的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Midinf : Midpnt { public new double func(double x) { return funk.funk(1.0 / x) / (x * x); } public Midinf(UniVarRealValueFun funcc, double aa,…

查询优化器内核剖析第一篇

SQL Server 的查询优化器是一个基于成本的优化器。它为一个给定的查询分析出很多的候 选的查询计划&#xff0c;并且估算每个候选计划的成本&#xff0c;从而选择一个成本最低的计划进行执行。实际上&#xff0c; 因为查询优化器不可能对每一个产生的候选计划进行优化&#xff…

IDEA集成Git相关操作知识(pull、push、clone)

一&#xff1a;集成git 1&#xff1a;初始化git&#xff08;新版本默认初始化&#xff09; 老版本若没有&#xff0c;点击VCS&#xff0c;选中import into Version Controller中的Create git Repository(创建git仓库)&#xff0c;同理即可出现git符号。 也可查看源文件夹有没有…

【力扣每日一题】2023.8.30 到家的最少跳跃次数

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一只跳蚤&#xff0c;我们可以操控它前跳 a 格或是后跳 b 格&#xff0c;不能跳到小于0的位置&#xff0c;有一些被禁止的点不…

系统架构设计高级技能 · Web架构

现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 Web架构 一、Web架构介绍1.1 Web架构涉及技术1.2 单台服务…

【JAVA】什么是异常

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 异常 1. 什么是异常1.1 概念1.2 异常的体…

函数名称add 与 add 作为参数传入的区别与探讨

在C和C中&#xff0c;函数名本身就是一个指向该函数代码的指针。因此&#xff0c;当你以函数名作为参数传递给其他函数时&#xff0c;实际上你传递的是该函数的地址。 对于你的代码&#xff0c;add是一个函数&#xff0c;&add是该函数的地址。由于add本身就代表了函数的地…

深圳产品展示视频拍摄一站式服务

产品展示视频拍摄一站式服务是指一家专业的拍摄制作公司或团队提供从策划、拍摄到后期制作的全方位服务&#xff0c;以满足客户的产品展示需求。这种服务通常包括以下方面&#xff0c;由产品展示视频制作公司老友记小编从以下几个方面为您整理&#xff1a; 1.策划和预制阶段&a…

RSA算法与错误敏感攻击

参见《RSA 算法的错误敏感攻击研究与实践》 RSA 算法简介 RSA 算法原理&#xff1a; 1&#xff09; RSA 算法密钥产生过程 &#xff08;1&#xff09;系统随机产生两个大素数 p p p 和 q q q&#xff0c;对这两个数据保密&#xff1b; &#xff08;2&#xff09;计算 n p …

小米面试题——不用加减乘除计算两数之和

前言 &#xff08;1&#xff09;刷B站看到一个面试题&#xff0c;不用加减乘除计算两数之和。 &#xff08;2&#xff09;当时我看到这个题目&#xff0c;第一反应就是感觉这是一个数电题目。不过需要采用C语言的方式编写出来。 &#xff08;3&#xff09;不过看到大佬的代码之…

代价高昂的 IT 错误:识别并避免供应商锁定

陷入不提供所需服务的云服务器合同中可能会非常痛苦、令人沮丧且成本高昂。 供应商锁定是提供商难以切换的地方&#xff0c;这意味着企业迁移到新供应商的成本太高、破坏性太大或耗时。 这使得公司受到供应商的摆布&#xff0c;尽管该服务可能无法提供他们所需的可靠性或可扩…

【高性能计算】opencl语法及相关概念(四):结合opencv进行图像高斯模糊处理

目录 高斯模糊简介主函数&#xff1a;host端设备端函数&#xff1a;mywork.cl效果图对比 高斯模糊简介 高斯模糊是一种常用的图像处理技术&#xff0c;用于减少图像中的噪点和细节&#xff0c;并实现图像的平滑效果。它是基于高斯函数的卷积操作&#xff0c;通过对每个像素周围…

亚马逊美国站上半年“日本商店”畅销品类了解一下吧!

近日&#xff0c;亚马逊美国站公布了2023年上半年“日本商店”&#xff08;JAPAN STORE&#xff09;的热门品类以及各品类销量排名前三的商品。据了解&#xff0c;亚马逊与日本贸易振兴机构 (JETRO) 合作&#xff0c;于2021年11月推出“日本商店”&#xff0c;支持日本卖家出海…

Ansible之playbooks剧本

文章目录 一.playbooks介绍1.playbooks简述2.playbooks剧本格式3.playbooks组成部分4.运行playbooks及检测文件配置 二.模块实战实例1.playbooks模块实战实例2.vars模块实战实例3.指定远程主机sudo切换用户4.when模块实战实例5.with_items迭代模块实战实例6.Templates 模块实战…

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

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

vue去掉循环数组中的最后一组的某个样式style/class

vue去掉循环数组中的最后一组的某个样式style/class 需求&#xff1a;要实现这样的排列 现状 发现&#xff0c;最后一个格子并没有跟下面绿色线对齐。 最后发现 是因为 每个格子都给了 margin-right&#xff1a;36px&#xff0c;影响到了最后一个格子 所以要 将最后一个格子的…

OpenCV(十一):图像仿射变换

目录 1.图像仿射变换介绍 仿射变换&#xff1a; 仿射变换矩阵&#xff1a; 仿射变换公式&#xff1a; 2.仿射变换函数 仿射变换函数&#xff1a;warpAffine() 图像旋转&#xff1a;getRotationMatrix2D() 计算仿射变换矩阵&#xff1a;getAffineTransform() 3.demo 1.…

浅谈城市轨道交通视频监控与AI视频智能分析解决方案

一、背景分析 地铁作为重要的公共场所交通枢纽&#xff0c;流动性非常高、人员大量聚集&#xff0c;轨道交通需要利用视频监控系统来实现全程、全方位的安全防范&#xff0c;这也是保证地铁行车组织和安全的重要手段。调度员和车站值班员通过系统监管列车运行、客流情况、变电…

【Vue】集成百度地图

Vue 集成 百度地图 1、获取百度地图 ak 密钥 2、登录网址 https://lbsyun.baidu.com/ 3、注册百度地图开放平台账号&#xff0c;填写认证信息&#xff0c;并且创建应用 安装 npm install vue-baidu-map --savemain.js import BaiduMap from vue-baidu-map;Vue.use(BaiduM…

mybatisPlus多数据源方案

背景 在微服务李娜一般一个服务只有一个数据源&#xff0c;但是在有的老项目或者一些特定场景需要多数据源链接不同的数据库&#xff0c;本文以mybatisPlus为基础给出解决方案 多数据源场景分类 情形一&#xff1a;项目启动就确定了情形一&#xff1a;一些sass系统里面动态确…