目录
写在前面的话
一,对象的初始化
1.1构造函数内部是对象的初始化?
1.2初始化列表
必须在初始化列表中初始化的变量
在哪里初始化都可以的变量
1.3变量的声明给的是缺省值
1.4成员变量的初始化顺序由声明决定
1.5explict禁止单参数隐式类型转换
二,static静态成员
静态成员变量
静态成员函数
三,右元
3.1右元函数
3.1.1什么是右元函数
3.1.2流插入,流提取实现右元函数
3.2右元类
3.3内部类
小伙伴们大家好啊!这里是小菜鸡库森,今天依旧为大家带来有关类和对象的内容,那么我们废话不多说,直接进入主题 ~ !
之前,我们在构造函数中实现了对象的赋值。但是事实上,构造函数中只是给对象进行了赋值操作,并没有进行初始化操作,因为对于每个成员而言,它的初始化只有一次。
但是我们发现,有时候一些成员变量是不能在构造函数内部进行赋值的。比如下图所示:
如上图所示,报错信息提示,后面三个成员变量在构造函数内部是不能赋值的,也就是他们需要初始化了之后,才能赋值。
所以我们对于上面所述的三种成员变量其实有一种特殊的初始化形式的,那就是初始化列表。
那么首先,我们来了解一下有关初始化列表的基本格式,如下图所示:
那么如上图所示的,在构造函数的 { } 外面,首先是一个冒号,然后后面是变量,小括号中的值就是初始化的值,如果有多个变量需要初始化,则直接在后面加一个逗号,然后再将初始化值放在变量的小括号中。这就是初始化列表。
如上图我们看到,有三种类型的变量必须在初始化列表中初始化。
首先就是 const 修饰的常变量,因为常变量是不能赋值的,或者说只能有一次赋值的机会,那就是定义的时候,所以在构造函数内部是不可以赋值的,就只能在初始化列表中初始化。
其次,对于引用类型的变量,也是必须在定义的时候初始化;以及自定义类型变量,因为它在该类中是没有默认构造函数的,所以也是需要在初始化列表中初始化的。
那么对于上图所示的内置类型的变量,在初始化列表中初始化和在构造函数中初始化都是一样的,因为他们可以重复赋值。
相信一些下伙伴还是有问题的,比如下图所示:
比如这里明明不是给初值了吗?为什么还要在初始化列表中初始化呢?那么需要注意这里是变量的声明,不是变量的定义,所以这里的赋值操作,我们将其称为给缺省值,而非变量的初始化。
那么对于这个缺省值,我们需要做出以下解释:
非静态成员变量可以在声明时,给与缺省值,这是C++11新加入的。一定需要注意的是,这里的权限仅仅是非静态成员变量。
入题目所示,成员变量的初始化的顺序是由声明决定的,和它在初始化列表中的顺序无关。也就是说,不管你在初始化列表中先初始化了谁,初始化的顺序依旧是由它的声明决定的。
其实构造函数除了构造与初始化对象,同时还具有单参数隐式类型转换的作用!但是这种转换设计的并不好,所以后面 C++ 提出了 explict 来帮助我们修补这个漏洞。
那么在有一些函数中,有可能存在一个参数构造函数。此时,就有可能出现如下所示的情况:
就拿我们的日期类来做例子:
那么对于 d2 和 d3 对象而言,是可以的,因为年份也是整型。但是如果是浮点型,因为也是属于整型家族,所以这样的传参,不会报错,只是会有个警告,那如果我们没有关心警告,这个参数传递了之后,就导致我们的年份变成了浮点数,那么这样是肯定不被允许的。
那么如果要解决这个问题,首先我们想到的是,对于这样的传参,其实是有两个步骤的,先是2022.5 调用了构造对象,然后构造出了一个临时对象Date(2022.5),接着这个临时对象又去调用构造函数去初始化 d4 对象,所以这里其实是有两步的。而在这其中,就发生了隐式类型转换。
那么为了避免这种情况的发生,我们使用 explict 关键字来修饰构造函数。这样就可以很好的避免这样的错误的出现。
首先,static修饰的类成员称为类的静态成员。那么对于类的静态成员我们有以下说明:
static 修饰的类成员称为静态成员,static 修饰的成员函数称为静态成员函数,static修饰的成员变量称为静态成员变量。
那么首先对于静态成员变量而言,如下图所示:
首先声明一定是在类中,要不就不是类的静态成员变量了,而对于静态成员变量的初始化是在类外的,同时需要主要,初始化的时候不需要加 static 关键字,因为声明已经加了。
那么对于静态成员函数而言,因为它是静态的,是被所有类共享的,所以它是没有隐含的 this 指针的,也就是说,它只能访问静态成员变量。但是在类外访问时,需要使用类名或者类对象去调用,所以对于类的成员而言,是有public,private,protected这三个权限以及范返回值的。
正因为静态成员函数是没有 this 指针的,所以是不能访问任何非静态成员函数的。
那么接下来我来了解一下有关右元函数的问题。
那么对于右元函数来说呢,它本身是一种类外的全局函数,但是又由于一些问题,它需要访问类的成员函数,那么我们知道类的成员函数一般是私有的,因为这里涉及到类的封装性,所以在类外一般是不能访问类的成员变量的。
所以就需要将其声明在类中,然后再类外的函数实现,就可以访问类的成员函数了。我们将这种全局函数称为右元函数。
接下来我们通过流插入和流提取操作符的重载来理解一下这里关于右元函数的问题。
那么如上图所示,对于C++中的流插入来说,是上图左边所示的形式,那么也即是说,在真正调用的时候我们一般需要写成右边的等价式子,但是我们发现,右边的式子并不符合我们正常的操作符的调用逻辑,虽然这样写是可以的。
但是因为我们想让其和正常的操作符的调用差不多,而类函数的第一个参数一般会是隐含的this指针,所以我们不能直接写为 cout << d ,因此我们使用全局的右元函数来实现,如下图所示:
那么当我们将其定义为全局函数之后,就没有 this 指针的约束了,所以我们的参数位置是任意的。这样我们就可以直接将流插入和流提取操作符写成上面所述的位置了。
同样的,因为在类外访问成员变量是不可以的,所以接下来我们将其通过在函数前面加上关键字friend 的方式声明在类中。
这样就是表示这两个函数是该类的右元函数。可以在类访问类的私有成员变量,但是我们知道,其实右元函数也是一种破坏类的封装性方式,所以一般情况下,我们也不推荐使用它,当然对于流插入和流提取这样的操作,这样实现是有必要的。
那么在调用的地方我们就可以使用正常的操作符重载来调用了:
是的,除了右元函数之外,还有右元类。和右元函数类似,如下图所示:
那么我们看到,如果在 B 类在 A 类中声明 B 是 A 类的右元类,则 B 类可以在 A 类中访问 A 类的非公有成员;如果反过来也是同样的。但是如果我们将这两种右元都写出来(上图所示),那么就说明 A 和 B 互为右元类。
除此之外,还需要注意的是,右元类是不能传递的。
这里我们又提出了一个内部类的概念,那么什么是内部类呢?
所谓内部类,就是在一个类中嵌套着另一个类,但是外层类对于内层类没有任何的优越关系,不能通过对象等调用内层类的非公有成员,仅仅是在类外访问内层类的时候,需要加上外层类的的域作用限定。但同时,我们发现,其实内部类是外部类的右元类,所以符合所有右元类的规定。
如下图所示:
但是这里我们仍旧需要注意以下两个点:
1.内部类定义在外层类的什么位置都是可以的,不受外部类的访问权限约束。
2.在使用 sizeof 求外部类大小的时候,不包括内部类大小。
那么对于类和对象的基本内容到这里就基本结束啦!如果觉得本文对你有一丝丝的帮助的话,可否给个三连支持一下呢,小菜鸡祝你程序无bug,生活处处是晴天哦!