“三步走”带你拿下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++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

javaSE类和对象进阶用法举例详解

《javaSE类和对象进阶用法举例详解》JavaSE的面向对象编程是软件开发中的基石,它通过类和对象的概念,实现了代码的模块化、可复用性和灵活性,:本文主要介绍javaSE类和对象进阶用法的相关资... 目录前言一、封装1.访问限定符2.包2.1包的概念2.2导入包2.3自定义包2.4常见的包二、stati

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符