【C++进阶学习】第二弹——继承(下)——挖掘继承深处的奥秘

news2024/10/5 5:56:03

继承(上):【C++进阶学习】第一弹——继承(上)——探索代码复用的乐趣-CSDN博客

前言:

在前面我们已经讲了继承的基础知识,让大家了解了一下继承是什么,但那些都不是重点,今天,我们一起来挖掘一下继承底层的一些知识和一些极容易出错的点

目录

一、隐藏

1.1 隐藏的概念

1.2 隐藏的两种类型

二、派生类的默认成员函数

三、继承与友元

四、继承与静态成员

五、总结


一、隐藏

1.1 隐藏的概念

在 C++ 中,继承是一种机制,使得子类可以继承父类的成员变量和成员函数。然而,当子类中出现和父类同名的成员变量或成员函数时,会发生一种特殊的现象,即隐藏

隐藏是指:如果子类中出现了与父类同名的成员变量或成员函数,则子类中的这个成员会“隐藏”父类中的同名成员,使得父类中的同名成员在子类中不可见。

1.2 隐藏的两种类型

具体来说,有以下两种情况:

成员变量隐藏:
如果子类中出现了和父类同名的成员变量,则子类中的这个成员变量会隐藏父类中的同名成员变量。例如:

class Parent {
public:
    int a;
};

class Child : public Parent {
public:
    int a; // 此处的 a 会隐藏 Parent 中的 a
};

int main() {
    Child c;
    c.a = 10; // 此处修改的是 Child 中的 a,而不是 Parent 中的 a
    return 0;
}

成员函数隐藏:
如果子类中出现了和父类同名的成员函数,则子类中的这个成员函数会隐藏父类中的同名成员函数。例如:

class Parent {
public:
    void func() {
        cout << "Parent::func()" << endl;
    }
};

class Child : public Parent {
public:
    void func() {
        cout << "Child::func()" << endl;
    }
};

int main() {
    Child c;
    c.func(); // 此处调用的是 Child 中的 func(),而不是 Parent 中的 func()
    return 0;
}

需要注意的是,虽然子类中的成员会隐藏父类中的同名成员,但是父类中的成员仍然存在,只是在子类中不可见。如果想在子类中访问父类中被隐藏的成员,可以使用作用域运算符(::)来显式地指明要访问的成员所在的类。例如:

class Parent {
public:
    int a;
};

class Child : public Parent {
public:
    int a;
};

int main() {
    Child c;
    c.a = 10; // 此处修改的是 Child 中的 a
    c.Parent::a = 20; // 此处修改的是 Parent 中的 a
    return 0;
}

总之,在 C++ 中的继承中,隐藏是一种特殊的机制,需要注意避免误用。

二、派生类的默认成员函数

在 C++ 中,当我们定义一个类时,可以省略掉其中的成员函数的实现,而直接在类定义的外部提供其实现。这种情况下,如果我们不提供任何实现,那么 C++ 编译器会自动为我们提供一个默认的构造函数、析构函数和拷贝构造函数和拷贝赋值运算符。

对于派生类来说,情况也是类似的。当我们定义一个派生类时,如果我们不提供任何构造函数,那么 C++ 编译器会自动为我们提供一个默认的构造函数,其构造函数的参数列表和父类的构造函数一致。例如:

class Parent {
public:
    Parent(int a) : m_a(a) {}
    int m_a;
};

class Child : public Parent {
public:
    Child() : Parent(0) {} // 此处的构造函数会自动调用 Parent 的构造函数,并传入 0 作为参数
};

同时,如果我们没有提供任何析构函数,那么 C++ 编译器也会自动为我们提供一个默认的析构函数,其析构函数的函数体为空。例如:

class Parent {
public:
    ~Parent() {}
};

class Child : public Parent {
public:
    ~Child() {} // 此处的析构函数会自动调用 Parent 的析构函数
};

需要注意的是,如果我们提供了任何一个构造函数或析构函数,那么 C++ 编译器就不会再为我们提供默认的构造函数或析构函数了。这时如果我们需要使用默认的构造函数或析构函数,需要我们自己显式地提供。(析构顺序为先派生类再基类)

另外,对于拷贝构造函数和拷贝赋值运算符来说,如果我们没有提供任何拷贝构造函数和拷贝赋值运算符,那么 C++ 编译器会自动为我们提供一个默认的拷贝构造函数和拷贝赋值运算符,其行为是浅拷贝(Shallow Copy),即直接拷贝成员变量的值。例如:

class Parent {
public:
    Parent(int a) : m_a(a) {}
    int m_a;
};

class Child : public Parent {
public:
    Child(int a, int b) : Parent(a), m_b(b) {}
    int m_b;
};

int main() {
    Child c1(1, 2);
    Child c2(c1); // 此处调用的是 Child 的默认拷贝构造函数,直接拷贝 m_a 和 m_b 的值
    return 0;
}

但是,如果我们的类中有指针类型的成员变量,那么默认的拷贝构造函数和拷贝赋值运算符就会出现问题,因为它们只会拷贝指针的值,而不会拷贝指针所指向的内存。这时我们需要自己提供一个拷贝构造函数和拷贝赋值运算符,实现深拷贝(Deep Copy)。例如:

class Parent
{
public:
	Parent(int a)
		:_a(a)
	{}
	int _a;
};
class Child :public Parent
{
public:
	int* _b;

	Child(int a, int b)
		:Parent(a),
		_b(new int(b))         //深度拷贝
	{}
	~Child()
	{
		delete _b;
		_b = nullptr;
	}
	Child(const Child& c)
		:Parent(c)
	{
		_b = new int(*c._b);
	}
	Child& operator=(const Child& c)
	{
		if (this != &c)
		{
			_a = c._a;
			delete _b;
			_b = new int(*(c._b));			
		}
		return *this;
	}
	void Print()
	{
		cout << "_a:" << _a << " " << "_b:" << *_b << endl;
	}
};
int main()
{
	Child c1(1, 2);     
	Child c2(c1);      //此处调用的是c2的深度拷贝
	c2.Print();

	Child c3 = c1;
	c3.Print();
	return 0;
}

运行结果:

我们通过两张图来总结一下:

三、继承与友元

在C++中,友元关系不能被继承,因为友元关系是独立于类定义的,并不是类的成员。因此,如果在父类中声明了一个友元函数或友元类,子类无法继承这种关系。

下面是一些相关知识点:

1、友元函数不能是成员函数:友元函数不是类的成员函数,因此不能使用this指针,也不能直接访问类的私有成员。需要通过类的对象或引用来访问私有成员。
2、友元关系不具有传递性:如果A类声明了B类为友元,则B类不会自动成为A类的友元。
友元函数可以是模板函数:模板函数可以被声明为类的友元,这样模板函数可以访问类的私有成员。
3、友元类:如果一个类声明另一个类为友元,则该友元类的所有成员函数都可以访问原类的私有成员。
4、友元不能继承:由于友元关系不是类的成员,因此不能被继承。如果在父类中声明了一个友元函数或友元类,子类无法继承这种关系。但子类可以在自己的范围内重新声明该友元关系。
示例:

#include<iostream>
#include<string>
using namespace std;
class Base {
public:	
	friend void friendFunction(Base&);
protected:
	int value;
};

class Derived : public Base {
public:
	// friendFunction 在 Derived 中不是友元函数,需要重新声明
	friend void friendFunction(Derived&);
};

void friendFunction(Base& base) {
	// 可以访问 Base 的私有成员
	base.value = 10;
}

void friendFunction(Derived& derived) {
	// 可以访问 Derived 的私有成员
	derived.value = 20;
}

int main() {
	Derived derived;
	friendFunction(derived);
	return 0;
}

在上面的示例中,由于友元关系不能被继承,因此在Derived类中需要重新声明friendFunction函数为友元函数,以便在Derived类的实例上调用该函数。

四、继承与静态成员

在 C++ 中,类可以包含静态成员变量和静态成员函数,其中静态成员变量属于类本身,而不是类的某个对象,因此它们可以在不创建类对象的情况下被访问。

当一个类继承另一个类时,子类可以继承其父类的静态成员,并且可以在子类中重新定义这些静态成员。在这种情况下,子类和父类各自拥有自己的静态成员变量,它们之间没有任何关系。

下面是一个简单的例子:

#include<iostream>
using namespace std;

class Parent {
public:
	static int a;
};

int Parent::a = 10;      //静态成员的定义只能在类外进行

class Child : public Parent {
public:
	static int a;      //类中只能声明静态成员
};

int Child::a = 20;       //静态成员的定义只能在类外进行

int main() {
	cout << Parent::a << endl;    //输出10
	cout << Child::a << endl;     //输出20
	return 0;
}

运行结果:

在上面的例子中,Parent 类和 Child 类都有一个静态成员变量 a,它们各自拥有自己的实现。在 main 函数中,我们可以直接通过类名来访问这些静态成员变量。

       需要注意的是,如果子类中没有重新定义父类的静态成员变量,那么子类可以直接访问父类的静态成员变量,例如 Parent::a。如果子类重新定义了父类的静态成员变量,那么子类只能访问自己的静态成员变量,例如 Child::a。

       此外,静态成员函数也可以继承,并且可以在子类中重新定义。在子类中重新定义父类的静态成员函数时,子类的静态成员函数会隐藏父类的静态成员函数,因此如果在子类中需要调用父类的静态成员函数,需要使用作用域运算符 :: 来显式地调用。

       还有一个需要注意的点就是,类中只能声明静态成员,静态成员的定义只能在类外进行。

总之,在 C++ 中,静态成员在继承中的行为与普通成员有所不同,需要注意其使用方法。

五、总结

以上就是C++继承中需要额外注意的点,此外,还有一个很重要的知识点我们还没讲到——多继承、菱形继承、虚拟继承,这几个知识点是有很大关联性的,且我们在平时使用继承时也很容易出错,鉴于篇幅问题,这几个问题会在下一篇单拎出来来讲,今天的内容就到此为止。

感谢各位大佬观看,创作不易,还请各位大佬一键三连!!!

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

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

相关文章

半监督学习

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 介绍一、Self Training自训练1、介绍2、代码示例3、参数解释 二、Label Propagation&#xff08;标签传播&#xff09;1、介绍2、代码示例3、参数解释 三、Label Spread…

突然挣不到钱了?带货主播大降薪,有人收入“腰斩”!时薪低至20元,“不如街头发小广告”

韭菜都想来割韭菜了&#xff0c;从00后到60后都在直播带货&#xff0c;部分业内人士认为不懂行的商家以及海量素人主播的加入&#xff0c;拉低了行业的平均薪酬。 2024年的电商年中大促接近尾声&#xff0c;电商直播市场再次成为广为关注的焦点。然而&#xff0c;与热闹的“618…

解放代码:识别与消除循环依赖的实战指南

目录 一、对循环依赖的基本认识 &#xff08;一&#xff09;代码中形成循环依赖的说明 &#xff08;二&#xff09;无环依赖的原则 二、识别和消除循环依赖的方法 &#xff08;一&#xff09;使用JDepend识别循环依赖 使用 Maven 集成 JDepend 分析报告识别循环依赖 &a…

C++使用spdlog输出日志文件

参考博客&#xff1a; 日志记录库 spdlog 基础使用_spdlog 写日志-CSDN博客 GitHub - gabime/spdlog: Fast C logging library. 首先在github上下载spdlog源码&#xff0c;不想编译成库的话&#xff0c;可以直接使用源码&#xff0c;将include文件夹下的spdlog文件夹&#x…

CSS选择符和可继承属性

属性选择符&#xff1a; 示例&#xff1a;a[target"_blank"] { text-decoration: none; }&#xff08;选择所有target"_blank"的<a>元素&#xff09; /* 选择所有具有class属性的h1元素 */ h1[class] { color: silver; } /* 选择所有具有hre…

116.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-解析结构数据的创建信息

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

智谱AI GLM-4V-9B视觉大模型环境搭建推理

引子 最近在关注多模态大模型&#xff0c;之前4月份的时候关注过CogVLM&#xff08;CogVLM/CogAgent环境搭建&推理测试-CSDN博客&#xff09;。模型整体表现还不错&#xff0c;不过不支持中文。智谱AI刚刚开源了GLM-4大模型&#xff0c;套餐里面包含了GLM-4V-9B大模型&…

springboot+vue+mybatis酒店房间管理系统+PPT+论文+讲解+售后

随着现在网络的快速发展&#xff0c;网络的应用在各行各业当中它很快融入到了许多商家的眼球之中&#xff0c;他们利用网络来做这个电商的服务&#xff0c;随之就产生了“酒店房间管理系统”&#xff0c;这样就让人们酒店房间管理系统更加方便简单。 对于本酒店房间管理系统的…

本地localhost与目标地址跨域问题的解决方法

场景 开发过程中遇到一个控件&#xff0c;上传图片到某cdn&#xff0c;目标地址对localhost会有跨域问题&#xff1a; 解决方法 参照此博客&#xff0c;将本地地址定义为某网址&#xff0c;如abc&#xff1a; win10修改本地host文件&#xff0c;用以增加自定义本地访问域名12…

不做题,可以通过PMP考试吗?

如果你想要避免浪费3900元并且不想再支付2500元的补考费&#xff0c;我建议你多做一些PMP考试的练习题&#xff1b;如果你不在意这些费用&#xff0c;也可以选择资助我&#xff0c;嘿嘿。不做题的话&#xff0c;通过PMP考试的几率是非常小的。因为做题是检验我们学习成果并发现…

躬行践履始玉成,行而不辍终致远 | 中创算力季度优秀员工表彰大会

蓬勃发展&#xff0c;根基在人。在中创发展的道路上&#xff0c;有初心不改的领导者、有披星戴月的业务标杆、也有默默坚守的员工&#xff0c;他们扎根中创&#xff0c;用努力、拼搏、坚持&#xff0c;在中创的历程上镌刻下 属于自己的一份印记&#xff01; 为了表彰优秀&…

电商商品项目||电商竞品分析|主流电商商品API接口在竞品分析中的重要应用

竞争数据采集 竞争数据是对在电子商务业务中彼此存在竞争关系的商家、品牌、产品(即竞争对手&#xff09;等各项运营数据的总称&#xff0c;在电子商务企业的经营过程中&#xff0c;对竞争对手进行分析可以帮助决策者和管理员了解竞争对手的发展势头&#xff0c;为企业成略制定…

多尺度特征提取:原理、应用与挑战

多尺度 多尺度特征提取&#xff1a;原理、应用与挑战**原理****应用****挑战****总结** 多尺度特征提取&#xff1a;原理、应用与挑战 在计算机视觉、自然语言处理和信号处理等领域&#xff0c;有效地捕捉和解析数据的多种尺度特性是至关重要的。多尺度特征提取是一种技术&…

24年下半年安徽教资认定准确时间和流程

安徽教资认定准确时间 网上报名时间&#xff1a; 第一批次&#xff1a;4月8日至4月19日17时 第二批次&#xff1a;6月17日至6月28日17时 注意&#xff1a;符合安徽省申请条件的普通大中专院校2024届全日制毕业生&#xff0c;应统一选择6月17日至6月28日17时的时间段进行网上报名…

VM4.3 二次开发02 方案加载、执行及显示

效果 这是二次开发的第二个文章&#xff0c;所以不重复说明环境配置相关的内容。如果不懂的可以看本专栏的上一个文章。 海康视觉算法平台VisionMaster 4.3.0 C# 二次开发01 加载方案并获取结果-CSDN博客 界面代码 <Window x:Class"VmTestWpf.App.MainWindow"x…

浏览器必装插件推荐:最新版Simple Allow Copy,解除网页复制限制!

经常在网上找资料的朋友&#xff0c;尤其是学生党&#xff0c;总会遇到一个问题&#xff1a;很多资料网站的文字是禁止复制的。于是大家通常会使用各种文字识别软件来图文转换&#xff0c;或者直接手打。 今天这款小工具&#xff0c;可以轻松复制各种氪金网站上的任何文字&…

爆肝整理AI Agent:在企业应用中的6种基础类型

AI Agent智能体在企业应用中落地的价值、场景、成熟度做了分析&#xff0c;并且探讨了未来企业IT基础设施与架构如何为未来Gen AI&#xff08;生成式AI&#xff09;做好准备。在这样的架构中&#xff0c;我们把最终体现上层应用能力的AI Agent从不同的技术要求与原理上分成了几…

考试系统Spring Security的配置

设置Spring Security配置类 1、设置包括认证、授权方法 protected void configure(HttpSecurity http) throws Exception {http.headers().frameOptions().disable();List<String> securityIgnoreUrls systemConfig.getSecurityIgnoreUrls();String[] ignores new Str…

重生奇迹mu圣导师介绍

出生地&#xff1a;勇者大陆 性 别&#xff1a;男 擅 长&#xff1a;统率&宠物使用 转 职&#xff1a;祭师&#xff08;3转&#xff09; 介 绍&#xff1a;当玩家账号中有一个Lv250以上角色时&#xff0c;便可以创建职业为圣导师的新角色&#xff0c;圣导师每升一级获得…

Trying to access array offset on value of type null

主要原因是版本7.4以后PHP解析器会对null类型的下标访问直接报错 背景&#xff1a; laravel框架 同时使用了扩展A和扩展B 扩展A要求 php>7.4,同时扩展B的对null类型的下标访问不兼容php7.4 修改扩展B不太现实&#xff0c;毕竟扩展B中有太多的对null类型的下标访问。 解决…