“三步走”带你拿下C++类与对象(下)

2024-04-20 17:04
文章标签 c++ 对象 拿下 三步走

本文主要是介绍“三步走”带你拿下C++类与对象(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在学习了“上”篇和“中”篇后,我们对类和对象以及一些析构函数有了一定的理解,本文我们将继续深入讲解有关的其他内容。

一、初始化列表的引入

我们以之前的队列为例子(创建两个队列一个用于入栈一个用于出栈)

这个myqueue对内置类型不做处理,对自定义类型会调用其相应的构造函数,这是我们根据之前的知识得来的结论,但现在如果我们的栈不提供默认构造呢?(声明一下:在栈中,我们之前写的显示构造函数是全缺省的开辟空间的函数)此时,myqueue也无法生成其对应的构造,为了使程序正常运行,办法就是在myqueue下的类显示地去写构造,也就引入了初始化列表。

初始化列表的格式:冒号开始,逗号分割,写在构造函数的下方,如图所示

这里作者的理解是,因为stack的类已经没有默认构造函数了,它的同名函数可能是一个需要传参才能进行初始化的函数。此时需要我们写一个queue的构造函数,通过走这个函数来进行对stack类的初始化。注意,下面的{}不可省略。这样,内置类型_size就初始化为0,而作为自定义类型pushst和popst此时就可以调用他们自己创建的类之下的构造函数了(因为已经传参n了),当然,这个queue处写缺省值也是可以的。

初始化列表和函数体内初始化也可以混用,比如上面也可以这么写

初始化列表的本质可以理解为每个对象中成员定义的地方。(这里再强调一下,在queue这个类中,private中的成员只是声明,并没有开空间,而在初始化列表的地方就是将声明的成员进行定义,也就是开空间)

然而,有些对象必须在初始化列表进行初始化,在函数体内初始化就会报错。

比如1.const成员对象

这是因为,const变量必须在定义时被初始化,而我们刚才说初始化列表是成员定义的地方,所以要在上方初始化而不是函数体内部。

2.引用的对象

为了便于引用的初始化,我们增加一个形参。 

3.没有默认构造自定义类型成员(必须显示传参调构造)

——————————————————————————

初始化列表,不管我们写不写,每个对象都会走一遍,对于自定义类型就会去调用默认构造

即使我的初始化列表出什么也没写他也会将每个对象走一遍(此时stack类已经有默认构造函数才没有报错)

我们来看下面的一端代码

首先A类进行初始化,走初始化列表,由于main函数中传了参数所以_a1被初始化为1,然后_a1又作为参数进行_a2的初始化,按道理来讲运行结果应该是1 1,但结果确实1和随机值,为什么呢?关于初始化列表最后一个特点:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。也就是说,在这个程序中,因为a2先声明,我们在走初始化列表的时候先走a2的初始化,由于此时a1是随机值所以a2的初始化为随机值,然后a1被初始化为1。至此,初始化列表的相关知识基本就这么多。接下来我们来看一个有意思的东西。

第一行是进行初始化,第二行是我们之前讲过的拷贝构造,可是第三行呢?为什么它不会报错。其实背后的猫腻是——隐式类型转换。也就是说,1并没有直接赋值给aa3,而是先构造一个A的临时对象,在用这个临时对象拷贝构造aa3,在这里进行了优化,也就是连续构造加拷贝构造优化为了直接构造。这个与我们之前学过的内置类型就对得上了。那再看看下面这个

56行的引用赋值为什么就会报错呢?首先这里也肯定有构造,也就是2创建一个A类型的临时对象,但不同的是这里面就没有拷贝构造了。也就是说aa4引用的是类型转换中用2构造的临时对象。但报错的原因其实是权限的放大,所以我们在前面加const即可。

这个东西到底有啥用呢?我们引入一个实例,栈的压栈操作。

这是正常写法,但不足的是,它首先要构造,然后作为参数传参又要拷贝构造造成浪费。此时,我们上面的操作就起作用了。

209行代码就可以平替207+208行,这样就直接实现相同的压栈操作了(2的隐式类型转换给了push函数,且注意要加const否则会报错,因为会造成权限的放大)那多参数的怎么搞呢?这里是这样规定的

同理,237+238行与240的压栈操作结果相同。

二、explicit关键字

如果我们不想进行隐式类型转换,把构造函数用explicit关键字修饰即可。

三、静态成员变量与静态成员函数

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量

如果我们去计算这个类的大小会发现是8而不是12,因为静态成员变量在静态区存储,类中不存储成员函数,不存储静态成员变量。此时我们不能在此处给缺省值,因为这个类走初始化列表的时候是不对静态成员变量进行初始化的。应该在全局进行定义。我们想访问的话,用以下两种办法均可

注意,此时是对静态成员变量进行了public处理,如果放在private中就会无法访问,但也有静态成员变量即是private同时我们也能访问的办法,用静态成员函数。

用static修饰的 成员函数,称之为静态成员函数。

注意,在静态成员函数中,我们无法访问类中其他对象的成员,只能访问静态成员,因为在静态成员函数中是没有this指针的。

这样就可以解决问题了。我们再总结一下静态成员变量和函数的一些特性:1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区   2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明   3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问   4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员   5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

四、友元

1.我们在(中)篇已经讲解了它的语法和用途,在此就简单再回顾一下即可,我们在实现日期类的时候,为了输出类中的内容,实现<<和>>运算符重载函数,发现为了符合我们所认知的cout和cin用法,这两个函数只能写在类域的外部,这就造成了无法访问类中成员的问题,为了解决我们引入了友元。

2.友元类

我们有时候创建两个类的时候,会想在其中一个类中去访问另一个类的成员,显然这是不可以的,但我们把它写成友元类即可,它的写法和友元函数相同 friend class 类名,可以写在想访问的类的任意位置。

这个地方声明的位置不要颠倒,这里是date类可以访问time类的成员,而time类仍无法访问date类 的成员。

五、内部类

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。它仅仅是受到外部类的类域限制。

看,我们在定义B类的变量时不可以直接去像A类那样写,就是因为类域的限制。但如果计算A类的大小,B类中的成员变量并不算入其中,这也进一步说明内部类和外部类的独立性。

但是B天生就是A的友元,这也是内部类的一个特点。内部类随便访问外部类,但外部类不可访问内部类。

六、匿名对象(了解即可)

假设我们随便写一个类,然后创建一个变量,但有时候我们创建的变量没有名字,就叫匿名对象,我们来感受一下他们的差别

35行就是匿名,37行就是我们平常所定义的变量。那么他们在功能上有什么差别呢?

匿名对象的生命周期只在当前这一行,也就是说代码走到35行时开始初始化,构造,一旦走到36行它就调析构函数。而且,我们用匿名对象去调用成员函数也可以在一行实现

————

此系列到此就完结撒花了,希望朋友们多多点赞,后续继续更新高质量文章。

这篇关于“三步走”带你拿下C++类与对象(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920832

相关文章

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

C++实现获取本机MAC地址与IP地址

《C++实现获取本机MAC地址与IP地址》这篇文章主要为大家详细介绍了C++实现获取本机MAC地址与IP地址的两种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实际工作中,项目上常常需要获取本机的IP地址和MAC地址,在此使用两种方案获取1.MFC中获取IP和MAC地址获取

C/C++通过IP获取局域网网卡MAC地址

《C/C++通过IP获取局域网网卡MAC地址》这篇文章主要为大家详细介绍了C++如何通过Win32API函数SendARP从IP地址获取局域网内网卡的MAC地址,感兴趣的小伙伴可以跟随小编一起学习一下... C/C++通过IP获取局域网网卡MAC地址通过win32 SendARP获取MAC地址代码#i