【c++】继承学习(二):探索 C++ 中派生类的默认机制与静态成员共享

news2024/11/20 0:39:39

Alt

🔥个人主页Quitecoder

🔥专栏c++笔记仓

Alt

目录

  • `1.派生类的默认成员函数`
  • `2.继承与友元`
  • `3.继承与静态成员`

朋友们大家好,本篇文章我们来学习继承的第二部分

1.派生类的默认成员函数

在这里插入图片描述
来看下面的类:

class Person
{
public:
	Person(const char* name = "jason")
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};

class Student : public Person
{
public:
protected:
	int _num; //学号
};
  1. Student对象生成的默认构造函数,对内置类型不做处理,对自定义类型调用它的默认构造函数,规则和以前一样
    在这里插入图片描述
    在这里插入图片描述

派生类里面,把父类成员当做一个整体

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用

比如的父类的构造函数修改成这种:

Person(const char* name)
	: _name(name)
{
	cout << "Person()" << endl;
}

现在父类没有默认构造,会编译报错

在这里插入图片描述

这时候需要子类来完成构造函数:

Student(const char* name, int num)
	: Person(name)
	, _num(num)
{
	cout << "Student()" << endl;
}

这段代码功能:

基类初始化:
Person(name) 调用了基类 Person 的构造函数,并传递了 name 参数,这确保了 Person 类的成员 _name 被正确初始化。写成 Person(name) 就是指示编译器使用 Person 类的接受 const char* 参数的构造函数。如果不这样做,基类成员 _nameStudent 对象构建过程中不会被初始化。

注意

这里不能这样初始化:

Student(const char* name, int num)
	: _name(name)
	, _num(num)
{
	cout << "Student()" << endl;
}

父类成员需要当做一个整体的一个自定义类型的成员,不能单独对它的成员处理

_name 是基类 Person 的一部分,派生类 Student 没有直接的权限去初始化它。应该使用基类构造函数来初始化

确保基类的构造函数被调用是继承中非常重要的一部分,因为只有基类的构造函数知道如何正确初始化基类定义的成员。上面的修改确保当创建Student 类的对象时,它会首先调用 Person 类的构造函数初始化 _name,然后初始化派生类 Student 的 _num 成员

派生类这里分成了两个部分:父类和自己,父类的调用父类构造函数初始化

成员变量的初始化顺序是根据它们在类定义中出现的顺序,而不是初始化列表中的顺序。因此,基类的构造函数总是首先被调用,再是派生类中定义的成员变量

  1. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化一般情况下默认生成的就够用,如果涉及到深拷贝,就需要自己显示实现
Student(const Student& s)
   : Person(s)
   , _num(s._num)
{
   cout << "Student(const Student& s)" << endl;
}

在这里插入图片描述

  1. 派生类的operator=必须要调用基类的operator=完成基类的复制
Student& operator = (const Student& s)
{
	cout << "Student& operator= (const Student& s)" << endl;
	if (this != &s)
	{
		Person::operator =(s);
		_num = s._num;
	}
	return *this;
}

这里同名函数构成了隐藏

  1. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序
~Student()
{
	cout << "~Student()" << endl;
}

上面的函数我们都进行了显示调用,但是析构函数不可以
在这里插入图片描述
Student 的析构函数被调用并完成执行后,Person 的析构函数将随即被自动而且默认地调用。这样的设计可以防止基类成员被重复释放或者提前释放,从而导致潜在的错误和资源泄漏

  1. 派生类对象初始化:先调用基类构造再调派生类构造

  2. 派生类对象析构清理:先调用派生类析构再调基类的析构。

  3. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系

所以我们想显示调用就需要这样写:

~Student()
{
	Person::~Person();
	cout << "~Student()" << endl;
}

但是这里会导致一个问题,析构多调用了一次,就是因为析构函数先调用子类再调用父类的,子类析构函数结束后会自动调用父类析构

2.继承与友元

友元关系不能继承,基类友元不能访问子类私有和保护成员

class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

这里会编译错误:

在这里插入图片描述

基类将某些函数或类声明为友元,这个友元关系不会自动传递给从派生)。派生类需要自己明确声明哪些函数或类是它的友元

如何解决编译错误:

要解决 Display 函数不能访问 Student 类的 _stuNum 成员的问题,可以在 Student 类中也声明 Display 为友元:

class Student : public Person
{
public:
    friend void Display(const Person& p, const Student& s);
protected:
    int _stuNum; // 学号
};

现在,Display 函数是 PersonStudent 两个类的友元,可以访问两个类的保护成员

3.继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;

class Student : public Person
{
protected:
	int _stuNum; // 学号
};

class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};
void TestPerson()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

静态成员是与类本身关联的,而不是与类的单个实例相关联。静态成员变量在所有实例中共享,而静态成员函数可以在没有类实例的情况下直接通过类名调用。当静态成员被继承时,派生类共享同一个静态成员副本,因为静态成员是属于类的,不属于类的任何具体对象

在上面代码中,Person 类有一个静态成员 _count,它被用来统计该类的实例数量。每当创建一个 Person 类的实例或者它的派生类的实例时,构造函数都会递增 _count,因此 StudentGraduate 的示例也会递增 _count

分析:

  1. Person::_count 是静态成员变量,并且初始化为 0。它统计 Person 及其派生类 StudentGraduate 的对象个数。

  2. Student 类继承自 Person,没有定义新的静态成员变量,因此它共享 Person 类的静态成员 _count

  3. Graduate 类继承自 Student,也没有定义新的静态成员变量,因此它同样共享 Person 类的静态成员 _count

void TestPerson()
{
    Student s1; // 在构造时, Person::_count 变为 1
    Student s2; // Person::_count 变为 2
    Student s3; // Person::_count 变为 3
    Graduate s4; // Person::_count 变为 4
    cout << " 人数 :" << Person::_count << endl; // 输出 " 人数 :4"

    Student::_count = 0; // 重置 Person::_count 为 0

    cout << " 人数 :" << Person::_count << endl; // 输出 " 人数 :0"
}

TestPerson 函数中创建了三个 Student 对象和一个 Graduate 对象,每次构造函数调用都会递增 _count,因此打印 _count 的结果为 4。

然后,将静态成员 _count 通过 Student 类重置为 0。注意,这里使用 Student::_count 访问的实际上还是 Person 类的静态成员 _count,因为 Student 并没有重新定义它。这表明无论通过类 Person 还是它的任何派生类访问静态成员 _count,结果都是相同的。因此,第二次打印 _count 的结果是 0

静态成员的继承性质:静态成员在基类及其派生类之间是共享的,而不是每个派生类都有独立的静态成员副本。因此,无论是在基类还是派生类中访问静态成员,访问的都是同一个数据。在设计类层次结构时,这一点非常重要,因为静态成员的行为可能会影响整个类族

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

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

相关文章

K8S执行完毕kubectl init xxx 执行 kubectl get ns 报错才connect: connection refused

问题场景&#xff1a; 在安装完毕K8S之后&#xff0c;执行 kubectl get ns 报错&#xff1a; [rootmaster ~]# kubectl get pods E0501 08:34:55.770030 11268 memcache.go:265] couldnt get current server API group list: Get "https://192.168.1.100:6443/api?ti…

深度学习项目实战:Python深度强化学习求解动态旅行商问题

深度强化学习&#xff08;Deep Reinforcement Learning,DRL&#xff09;可以用于解决优化问题&#xff0c;尤其是具有复杂、高维度的状态空间和动作空间的问题。它结合了深度学习的强大表示能力和强化学习的学习框架&#xff0c;深度神经网络可以学习复杂的特征和模式&#xff…

【深耕 Python】Quantum Computing 量子计算机(1)图像绘制基础

一、绘制静止图像 使用matplotlib库绘制函数图像y sin(pi * x): import math import matplotlib.pyplot as pltx_min -2.0 x_max 2.0N 1000x1 [] y1 []for i in range(N 1):x x_min (x_max - x_min) * i / Ny math.sin(math.pi * x)x1.append(x)y1.append(y)plt.xl…

基于51单片机的交通灯设计—可调时间、夜间模式

基于51单片机的交通灯设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.四方向数码管同时显示时间&#xff1b; 2.LED作红、绿、黄灯 3.三个按键可以调整红绿灯时间&#xff1b; 4.夜间模式&am…

循环神经网络模块介绍(Pytorch 12)

到目前为止&#xff0c;我们遇到过两种类型的数据&#xff1a;表格数据和图像数据。对于图像数据&#xff0c;我们设计了专门的卷积神经网络架构(cnn)来为这类特殊的数据结构建模。换句话说&#xff0c;如果我们拥有一张图像&#xff0c;我们 需要有效地利用其像素位置&#xf…

85、动态规划-零钱兑换

思路&#xff1a; 还是老样子&#xff0c;还是先使用递归方式来解&#xff0c;然后通过递归推动态规划。那递归如何设计? 定义一个递归方法&#xff1a;表示从index开始到N达到剩下的值&#xff08;目标值减去上一步的值&#xff09;做少可以得到数量是多少。int process(in…

快速幂笔记

快速幂即为快速求出一个数的幂&#xff0c;这样可以避免TLE&#xff08;超时&#xff09;的错误。 传送门&#xff1a;快速幂模板 前置知识&#xff1a; 1) 又 2) 代码&#xff1a; #include <bits/stdc.h> using namespace std; int quickPower(int a, int b) {int…

STM32单片机wifi云平台+温度+烟雾+火焰+短信+蜂鸣器 源程序原理图

目录 1. 整体设计 2. 液晶显示 3. Ds18b20温度传感器 4. Mq2烟雾传感器 5. 火焰传感器传感器 6. 蜂鸣器驱动控制 7. 按键 8. Gsm短信模块 9. Esp8266wifi模块 10、源代码 11、资料内容 资料下载地址&#xff1a;STM32单片机wi…

PR2019新建项目教程

一&#xff0c;新建项目&#xff1a; 设置工程名称&#xff0c;选择工程目录位置&#xff0c;其他默认&#xff1a; 二&#xff0c;新建序列 新建项->序列&#xff1a; 设置序列参数&#xff1a; 三&#xff0c;导出设置 设置导出参数&#xff1a;

vivado Aurora 8B/10B IP核(11)- 共享逻辑(Share Logic)

Vivado IDE 中的共享逻辑选项配置核心&#xff0c;包括可收集的资源&#xff0c;如收发器四路 PLL&#xff08;QPLL&#xff09;&#xff0c;收 发器差分缓冲区&#xff08;IBUFDS_GTE2&#xff09;以及核心或示例设计中的时钟和复位逻辑。 当选择了核心选项中的包含共享逻辑时…

QT防止自研软件被复制的基本操作(二)

参考一 自研软件为了防止被人任意复制传播&#xff0c;需要设置注册使用模式。基本原理&#xff1a;通过计算机的特异性编号&#xff0c;加上自己的编码&#xff0c;使用加密算法算出一个生成码。 一、计算机的特异性编号 硬盘的编号&#xff1a;最后一块硬盘的编号就行&#…

linux的基础入门(2)

环境变量 在Shell中&#xff0c;正确的赋值语法是没有空格的&#xff0c;即变量名数值。所以&#xff0c;正确的方式是&#xff1a; tmpshy 这样就将变量tmp赋值为"shy"了。 注意&#xff1a;并不是任何形式的变量名都是可用的&#xff0c;变量名只能是英文字母、…

开源AI名片商城小程序:打造卓越客户体验,引领留存与增长新潮流

在快速变化的商业环境中&#xff0c;如何有效地吸引并留住客户已成为每个企业面临的重要课题。为此&#xff0c;我们精心打造了一款创新的解决方案——开源AI名片商城小程序&#xff0c;它将内容培育、社交互动、产品展示与购买功能融为一体&#xff0c;为您的客户带来前所未有…

PS超美古风毛笔字体300款,PS毛笔字体笔触与笔刷素材

一、素材描述 本套PS素材&#xff0c;大小1.66G&#xff0c;2个压缩文件。 二、素材目录 &#xff08;一&#xff09;、PS毛笔字体笔触与笔刷素材.rar&#xff08;163.5M&#xff09; &#xff08;二&#xff09;、PS超美古风毛笔字体300款.rar&#xff08;1.5G&#xff09…

2.1 上海雷卯电子PLC

PLC&#xff08;可编程逻辑控制器&#xff09;像是工厂自动化系统的“大脑”&#xff0c;负责监控和控制各种生产过程。PLC 能够精确地协调各类设备的操作&#xff0c;实现生产流程的自动化和优化。通过编程&#xff0c;它可以根据不同的生产需求灵活调整控制逻辑&#xff0c;提…

【数据结构】这里有一份KMP算法优化的详细攻略,不要错过哦!!!

KMP算法优化 导读一、C语言实现next数组1.1 next数组的底层逻辑1.2 串的数据类型的选择1.3 函数三要素1.3.1 查漏补缺——数组传参1.3.2 小结 1.4 函数主体1.4.1 通过模拟实现1.4.1.1 算法思路1.4.1.2 代码实现1.4.1.3 算法测试 1.4.2 算法的缺陷1.4.3 算法优化1.4.3.1 算法思路…

蛇鹭优化算法(SBOA)-2024年4月SCI新算法-公式原理详解与性能测评 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 一、初始化阶段 二、蛇鹭的捕猎…

CSS精灵图、字体图标、HTML5新增属性、界面样式和网站 favicon 图标

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…

C++校招八股

c类的访问权限与继承方式 公有成员在任何地方都可以被访问&#xff0c;包括类的外部和派生类。受保护成员在类的内部和派生类中可以被访问&#xff0c;但在类的外部不可访问。 私有成员只能在类的内部访问&#xff0c;包括类的成员函数和友元函数&#xff0c;不允许在类的外部…

应用分层和企业规范

目录 一、应用分层 1、介绍 &#xff08;1&#xff09;为什么需要应用分层&#xff1f; &#xff08;2&#xff09;如何分层&#xff1f;&#xff08;三层架构&#xff09; MVC 和 三层架构的区别和联系 高内聚&#xff1a; 低耦合&#xff1a; 2、代码重构 controlle…