C++【继承】

news/2025/2/12 13:18:36/

C++继承概念

继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为父类,新建的类称为子类。继承代表了 is a 关系


继承示例

// 父类
class Animal {// eat() 函数// sleep() 函数
};//子类
class Dog : public Animal {// bark() 函数
};

继承形式:class 父类 : 继承方式 子类

继承方式: public、protected 或 private 其中的一个,使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public


分析继承代码,熟悉继承

#include <iostream>
using namespace std;//父类
class Shape 
{public:void setWidth(int w){width = w;}void setHeight(int h){height = h;}protected:int width;int height;
};// 子类          继承方式为公有继承
class Rectangle: public Shape
{public:int getArea(){ return (width * height); }
};int main()
{Rectangle Rect;  //创建子类对象Rect.setWidth(5);Rect.setHeight(7);cout << "Total area: " << Rect.getArea() << endl;return 0;
}

所以:子类不但会继承父类的成员变量,同时也会继承父类的成员函数
在这里插入图片描述


继承方式造成的一些具体问题

继承方式有三种:public、private、protected

公有继承(public):

当一个类派生自公有的父类,父类的公有成员也是子类的公有成员,父类的保护成员也是子类的保护成员,父类的私有成员子类继承过去了,但是语法上规定子类对象不管是在类内还是类外都不可以访问

父类private成员在子类中是不能被访问,如果父类成员不想在类外直接被访问,但需要在
子类中能访问,就定义为protected。父类的protected成员继承下来都可以在子类对象类内使用

#include <iostream>
using namespace std;class Person
{
public:void Print(){cout << "Print()" << endl;}
protected:void protected_print(){cout << "protected_print" << endl;}
private:void private_Print(){cout << "private_Print" << endl;}
};class Student : public Person  //公有继承
{
protected:int _stunum; // 学号
};int main()
{Student s;s.Print();  //公有继承:子类将父类的public成员继承之后,变成自己的的成员return 0;
}
保护继承(protected):

当一个类派生自保护父类时,父类的公有成员和保护成员将成为子类的保护成员

私有继承(private):

当一个类派生自私有父类时,父类的公有成员和保护成员将成为子类的私有成员

父类和子类对象赋值转换(public继承)

子类对象 可以赋值给 父类的对象 / 父类的指针 / 父类的引用。这里有个形象的说法叫切片或者切割。寓意把子类中父类那部分切来赋值过去。
父类对象不能赋值给子类对象,父类的指针可以通过强制类型转换赋值给子类的指针(子类指针在使用时可能会存在越界风险)。但是必须是父类的指针是指向子类对象时才是安全的

    Person p;Student s;p = s; // 这里不存在类型转换,语法支持Person* ptr = &s;Person& ref = s; //注意:这里父类修改给可以改变子类的数据

在这里插入图片描述

这不支持private继承和protected继承的子类与父类之间的赋值,是因为存在权限的改变。

class Person
{
public:string _name;int _age;char _sex;void Print(){cout << "_name  _age  _sex" << endl;}};class Student :private Person // 继承方式为private
{						    // 那么在Student中父类的成员都是私有的
protected:string _id;
};int main()
{Person p;Student s;p = s;                   // 将子类成员赋值给父类时,子类成员与父类成员的访问限定符不一样,所以无法赋值Person* ptr = &s;            Person& ref = s;         // protected继承同理return 0;
}

继承中的作用域

  • 在继承体系中父类和子类都有独立的作用域
  • 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 父类::父类成员 显示访问)
  • 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
    注意在实际中在继承体系里面最好不要定义同名的成员
  • 因为函数重载两个函数必须在同一个作用域当中
class Person
{
public:string _name;int _age;char _sex;void Print(){cout << "_name  _age  _sex" << endl;}};class Student :public Person
{
public:void Print(){cout << "_id" << endl;}
protected :string _id;
};// Student中的Print和Person中的Print不是构成重载,因为不是在同一作用域
// Student中的Print和Person中的Print构成隐藏,成员函数满足函数名相同就构成隐藏
int main()
{Student s;s.Print(); // 子类调用自己的打印函数s.Person::Print(); // 子类调用父类的打印函数(显式调用)return 0;
}

子类的默认成员函数

对于一个空类,经由编译器处理过后,会为它声明一个默认构造函数、一个拷贝构造函数、一个赋值运算符重载、一个析构函数,且这些函数都是public的。称之为默认成员函数。

默认构造函数:

会先(在初始化列表的位置)调用父类的默认构造函数,完成父类的创建,再完成子类的对象构造

在这里插入图片描述
在这里插入图片描述

构造函数:

如果父类没有默认构造函数(编译器自己提供的无参构造函数、自己定义的全缺省的构造函数、自己定义的无参构造函数),则需要在初始化列表显示调用父类的构造函数

如果此时父类定义了构造函数,但是父类的构造函数时无参的或者是全缺省的构造函数此时子类的构造函数可以定义也可以不用定义。

如果父类显示定义了构造函数,但是父类的构造函数不是无参的或者全缺省的构造函数,此时子类必须要定义自己的构造函数,并且需要在其子类的构造函数初始化列表位置显示调用父类的构造函数,目的就是为了完成从父类中继承下来的成员的初始化。
在这里插入图片描述

拷贝构造函数:

如果父类和子类的拷贝构造函数都是编译器生成的,会先(在初始化列表的位置)调用父类的拷贝构造函数。如果父类显示定义了,则需要在子类的拷贝构造函数中显示调用,否则不会完成对父类部分的值拷贝(如果不是在参数列表位置调用的,则会先自动调用父类的默认构造函数,如果父类无默认构造,则会报错)

在这里插入图片描述

赋值运算符重载

子类的operator=必须要显式调用父类的operator=完成父类的赋值。
因为子类和父类的运算符,编译器默认给与了同一个名字,所以构成了隐藏,所以每次调用=这个赋值运算符都会一直调用子类,会造成循环,所以这里的赋值显式调用父类的
在这里插入图片描述

析构函数
子类的析构函数会在被调用完成后自动调用父类的析构函数清理父类成员。因为这样才能保证子类对象先清理子类成员再清理父类成员的顺序。


http://www.ppmy.cn/news/1152633.html

相关文章

07_03文件系统怎么玩的

文件系统 Linux将文件系统分为了两层&#xff1a;VFS&#xff08;虚拟文件系统&#xff09;、具体文件系统&#xff0c;如下图所示&#xff1a; VFS&#xff08;Virtual Filesystem Switch&#xff09;称为虚拟文件系统或虚拟文件系统转换&#xff0c;是一个内核软件层&#…

【AI视野·今日Sound 声学论文速览 第二十五期】Fri, 13 Oct 2023

AI视野今日CS.Sound 声学论文速览 Fri, 13 Oct 2023 Totally 8 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Impact of time and note duration tokenizations on deep learning symbolic music modeling Authors Nathan Fradet, Nicolas Gutowski,…

GPT-4V的图片识别和分析能力

GPT-4V是OpenAI开发的大型语言模型&#xff0c;是GPT-4的升级版本。GPT-4V在以下几个方面进行了改进&#xff1a; 模型规模更大&#xff1a;GPT-4V的参数量达到了1.37T&#xff0c;是GPT-4的10倍。训练数据更丰富&#xff1a;GPT-4V的训练数据包括了1.56T的文本和代码数据。算…

C++项目-求水仙花数

求水仙花数 #include <iostream> using namespace std;int main() {int n 100;do {int a 0;int b 0;int c 0;a n % 10; //个位b n / 10 % 10; //十位c n / 100 % 10; //百位if (a * a * a b * b * b c * c * c n) {cout << n << endl;}…

LeetCode每日一题——Single Number

文章目录 一、题目二、题解 一、题目 136. Single Number Given a non-empty array of integers nums, every element appears twice except for one. Find that single one. You must implement a solution with a linear runtime complexity and use only constant extra …

css 块元素、行内元素、行内块元素相互转换

在HTML和CSS中&#xff0c;元素可以分为三类&#xff1a;块级元素&#xff08;Block-level Elements&#xff09;、内联元素&#xff08;Inline Elements&#xff09;和内联块级元素&#xff08;Inline-block Elements&#xff09;。 块级元素&#xff08;Block-level Elements…

CANoe创建仿真工程

CANoe创建仿真工程 写在前面仿真工程的创建创建工程添加CAN数据库添加系统变量创建面板创建网络节点为节点添加代码工程运行测试总结 写在前面 Canoe的安装不是特别方便&#xff0c;我是参加了松勤的培训课程&#xff0c;不仅需要安装软件还需要安装驱动&#xff0c;刚刚学习的…

mysql面试题49:MySQL中不同text数据类型的最大长度

该文章专注于面试&#xff0c;面试只要回答关键点即可&#xff0c;不需要对框架有非常深入的回答&#xff0c;如果你想应付面试&#xff0c;是足够了&#xff0c;抓住关键点 面试官&#xff1a;MySQL中TEXT数据类型的最大长度 在MySQL中&#xff0c;TEXT数据类型用于存储较大…