当前位置: 首页 > backend >正文

c++第七天 继承与派生2

这一篇文章主要内容是

派生类构造函数与析构函数

在派生类中重写基类成员

以及多继承

第一部分:派生类构造函数与析构函数

        当创建一个派生类对象时,基类成员是如何初始化的?

1.当派生类对象创建的时候,基类成员的初始化顺序

(1)先初始化基类部分,再初始化派生类自己的成员

(2)也就是说,派生类对象在构造时,首先会调用基类的构造函数,把基类的成员变量初始化好,然后才会执行派生类自己的构造函数与成员初始化。

那么一般是什么样子的呢?

class Base {
public:Base() { cout << "Base构造" << endl; }~Base() { cout << "Base析构" << endl; }
};class Derived : public Base {
public:Derived() { cout << "Derived构造" << endl; }~Derived() { cout << "Derived析构" << endl; }
};int main() {Derived d;return 0;
}

  是这样的格式,首先进行的是初始化工作,然后再进行派生。

这里输出的结果是

Base构造
Derived构造
Derived析构
Base析构

第二个问题:当派生类对象销毁时,析构的顺序是什么样子的?

先析构派生类自己的成员,再析构基类部分。

也就是说,派生类对象销毁时,先执行派生类的析构函数,然后再自动调用基类的析构函数。

总结口诀:

构造顺序:先基类,后派生

析构顺序:先派生,后基类 

①在创建派生类对象时,首先会调用基类的构造函数,然后再调用派生类的构造函数。

#include<iostream>class Base{
public:Base(){std::cout << "Base constructor called" << std::endl;}
};class Derived : public Base{
public:Derived(){std::cout << "Derived constructor called" << std::endl;}
};int main()
{Derived d;return 0;
}

只要创建派生类对象,必然会调用基类构造函数

②当基类的构造函数需要参数时,派生类的构造函数需要显式地调用基类的构造函数并传递参数。

#include<iostream>
using namespace std;class Base{
public:Base(int value){std::cout << "Base constructor called with value" << value << std::endl;}
};class Derived : public Base{
public:Derived(int baseValue,int derivedValue) : Base(baseValue){std::cout << "Derived construtor called with value" << derivedValue << std::endl;}
};
int main()
{Derived d(10,20);return 0;
}

③派生类构造函数中初始化列表的作用和使用方法。初始化列表可以用于调用

a.基类的构造函数,b.对象成员的构造函数,c.派生类自身的成员变量。

#include <iostream>
using namespace std;class Base {
public:Base(int d):data(d) { cout << "Base" << endl; }int getData() {return data;}
protected:int data;
};class Derived : public Base {
private:Base b;         //对象成员int c;
public:Derived():Base(0),b(1) { cout << "Derived" << endl; }Derived(int x) :Base(x),b(2*x),c(3*x) {  }
};int main()
{Derived d(10);cout << d.getData() << endl;return 0;
}

④默认构造函数和自定义构造函数。如果基类有默认构造函数,派生类的构造函数可以不显式调用基类的构造函数,编译器会自动调用基类的默认构造函数。但如果基类没有默认构造函数,派生类的构造函数必须显式调用基类的构造函数。

⑤析构函数的调用顺序:在销毁派生类对象时,首先会调用派生类的析构函数,然后再调用基类的析构函数。(一句话:和构造的顺序相反)

#include <iostream>
using namespace std;class Base {
public:Base(int d):data(d) { cout << "Base" <<data<< endl; }int getData() {return data;}~Base() { cout << "Base析构" <<data<< endl; }
protected:int data;
};class Derived : public Base {
private:Base b;//对象成员int c;
public:Derived():Base(0),b(1) { cout << "Derived" << endl; }Derived(int x) :Base(x), b(2 * x) { c = 3 * x; }~Derived() { cout << "Derived析构" << endl; }
};int main()
{Derived d(10);cout << d.getData() << endl;return 0;
}

输出结果

      1. 在派生类中重写基类成员

重写是指在派生类中定义一个与基类中同名、同参数列表、同返回类型的成员函数,以此来改变基类函数的行为。

重载是在同一类中,函数名相同但参数列表不同。

#include<iostream>
using namespace std;class Base{
public:void display(){std::cout << "This is the Base222 class." << std::endl;}
};class Derived : public Base{
public:void display(){cout << "This is the Base class" << endl;}};int main()
{Derived d;d.display();return 0;
}

重写的函数(派生类中)必须与基类函数的函数名、参数列表和返回类型完全相同。否则就会变成重载(要求参数不同)重写函数的访问权限可以不同,但通常建议保持一致。例如,基类函数是 public,派生类重写函数也用 public。

  1. 多继承

单继承:一个派生类只能有一个直接基类;当需要组合多个不同类的特性时,单继承无法直接实现,需多继承。即多继承具有更强的代码重用的能力。

class 派生类 : 继承方式 基类1, 继承方式 基类2, ... {

    // 成员声明

};

多继承下的构造函数与析构函数的调用顺序

 

#include<iostream>
using namespace std;class Speakable{
public:Speakable() {cout << "Speakable构造" << endl;}void speak() {cout << "Speaking..." << endl;}
};class Moveable{
public:Moveable() {cout << "Moveable构造" << endl;}void move() {cout << "moving..." << endl;}
};class Robot : public Speakable,public Moveable{
public:Robot() : Moveable(),Speakable() {}
};int main()
{Robot r;r.speak();r.move();return 0;
}

多继承的二义性:

#include<iostream>
using namespace std;
class A {public:void f() { cout << "A"; }
};
class B {
public:void f() { cout << "B"; }
};class AB :public A, public B {public://void f() { cout << "AB"; }
};
int main()
{AB ab;ab.B::f();ab.A::f();return 0;
}

错误提示:对“f”的访问不明确

解决的办法:就是在成员函数的前面加上类名::,说明使用从哪个基类继承的成员函数。

ab.B::f();

ab.A::f();

http://www.xdnf.cn/news/13215.html

相关文章:

  • 安全编程期末复习12(红色重点向下兼容)
  • 河南建筑安全员C证考试常见题及答案解析
  • 2.7 判断.lib和.a是静态库 还是动态库的导入库
  • 基于Docker部署MYSQL主从复制
  • RT_Thread——线程管理(下)
  • 数学公式中latex的粗体问题
  • vSphere环境ubuntu24.04虚拟机从BIOS切换为EFI模式启动
  • 链表反转示例代码
  • 每日算法刷题Day27 6.9:leetcode二分答案2道题,用时1h20min
  • 论文解析:一文弄懂U-Net(图像分割)!
  • WEB3全栈开发——面试专业技能点P5中间件
  • 华为智选携手IAM:突破技术边界,重塑智慧健康家居新时代
  • 苍穹外卖|学习笔记|day07
  • C#学习第29天:表达式树(Expression Trees)
  • 俩人相向而行,何时相遇问题思考。
  • 《创始人IP打造:知识变现的高效路径》
  • EXCEL 实现“点击跳转到指定 Sheet”的方法
  • 《Minio 分片上传实现(基于Spring Boot)》
  • 第三章 3.2 无线传感器网络的组成
  • 本周黄金想法
  • yaml读取写入常见错误 (‘cannot represent an object‘, 117)
  • STM32ADC模数转换器,让你的32可以测量电压!!!
  • Mobile ALOHA全身模仿学习
  • MyBatis-Plus 常用条件构造方法
  • 什么是WBS项目管理?wbs项目管理有哪些核心功能?
  • 生产管理系统开发:专业软件开发公司的实践与思考
  • linux驱动开发(3)-页面分配器
  • C++模版初阶
  • deepseek: 重装系统
  • 【C/C++】玩转正则表达式