欢迎光临散文网 会员登陆 & 注册

C++自制心得——类与对象下

2023-09-11 20:09 作者:这年头起名可真不容易  | 我要投稿

这一章就是一些琐碎的收尾工作,单个知识点不难,但是很多很杂,建议多练。

1.const成员函数

在整个传参过程中,为了保护数据,this指针很容易传成const this指针,然而这样的this指针无法调用非const成员函数,所以我们需要一种办法用const修饰函数的this指针

这种函数就是const成员函数,设计成员函数时,十分建议将所有不涉及数据修改的成员函数变成const成员函数,好处多多

C++的类型检查严格,func(int a)和func(const int a)会被视作函数重载,也就是说我们可以同时设计const与非const函数,一般情况下,这没有什么用,但如果你要实现读写功能分离,这会非常有用

当成员函数同时存在const版本与普通版本,普通对象会走普通版本,只有在没有普通版本时,普通对象才会走const版本,编译器会优先选择最匹配函数

slist.cpp

slist.h

test.cpp

在这里,对operator[]重载两个版本就可以实现普通对象可读可写,const对象只读

2. 取地址重载函数

operator&的重载函数,有两个,一个针对普通对象,一个针对const对象。作用就是提供对象地址,作为六大默认成员函数直接用编译器生成的就可以了,基本查无此人。为数不多的用途就是返回虚假地址(到底是什么极端情况才会用到这个功能)

取地址给你返野指针,就问你怕不怕,相当缺德的写法

3. 构造函数的隐式类型转换

C++11支持单参数构造函数的隐式类型转换,理论上的流程是这样的:先调用构造函数生成一个临时对象,再调用拷贝构造函数将临时对象拷贝给a2。但是比较新的编译器会做优化,直接调用构造函数生成a2。程序结果如下

我们换一种写法

这样写也是可以的,但是下面的不行

为什么,临时对象具有常性,只能用const引用接收,如果用普通引用接收,就构成权限的放大。

如果你想关闭这个功能,可以使用explicit关键字声明构造函数

C++11也支持多参数构造函数的隐式类型转换

就是数组初始化的方法,没什么可讲的

那么这个特性的用途是什么?举个例子,我现在想用一个顺序表存日期

(原代码只是把之前的日期类和顺序表类结合到了一起,加起来500多行,为了诸位的阅读体验,我就不放了)

显然,后面一种更省事,效率更高(优化后)

4. 匿名对象

匿名对象就是临时对象,结合之前的所学内容,它的特性有如下几点

1. 不需要名字(废话)

2. 生命周期极短(只在当前语句生效)

比方说这里有一个int型的临时对象,那么它的生命周期从41行开始,至42行结束。再准确一点,这个变量的构造始于func(int(10));语句的执行,析构于func(int(10));语句的结束,中间的部分就是它的生命周期

3. 具有常属性

所有的临时变量默认被const修饰

其主要调用方式有两种

1. 编译器自动生成

自动生成的临时变量一般用于承载传值返回中的返回值,值传递行为的实参值,或者隐式类型转换中的源变量值。综上,临时变量的作用就是充当一个中介变量,而中介变量显然没有必要修改,这也是为什么临时变量具有常属性的重要原因。

2. 程序员显式调用

定义一个临时对象只需要写成类型+(参数)的形式,如果没有参数也需要括号。无论是自定义类型,还是内置类型都能用这种方法人为生成临时变量。既然临时变量的作用就是充当中间人,那么它的使用场景往往透漏出一种一次性质感。

我现在只想打印一个日期,我为什么要费劲的定义一个局部变量,直接用临时变量不就行了。这只是临时变量最简单的用法,后面的内容会用到更多的临时对象,这里按下不表。

5. 静态成员

已经讲了四个内容了,想不想休息一下。哼哼,damn,咱们C++的特性就是多,不要不服气。

1. 静态成员变量

现在我想知道一个类的对象一共创建了几个,正在使用的有几个。感觉这个需求一点也不难嘛,安排

很好很好,但是倘若如此操作,阁下又将如何?

main函数(修改后)

这倒是个难题,有没有一种方式即可以将变量封装到类里,又可以让这个变量具备唯一性?用一下静态成员变量吧

首先我们知道静态变量单独存在于静态区,而非栈区,有了这个前置知识,有关于静态成员变量的特性就变的很好理解

1. 静态成员变量不会走初始化列表

首先初始化列表负责的是非静态成员变量的初始化,又因为每一个对象都需要独立的非静态成员变量,所以初始化列表与对象的创建过程强相关

但是静态变量不一样,所有的对象只需要唯一的静态成员变量,那么静态成员变量的创建显然不应由初始化列表负责,这是不符合逻辑的

毫无疑问,静态成员变量也不能通过在声明处给缺省值初始化,因为这种方式也要用到初始化列表。我们只能人工初始化静态成员变量(类中的东西只是声明,不存在实际空间)

2. 静态成员变量不会存储在对象中

理所当然,静态成员变量与成员函数唯一存在,无需存储于对象中

2. 静态成员函数

既然存在静态成员变量,那么静态成员函数又能做什么?

你能不能在不额外实例化对象的情况下打印两个静态成员变量吗?好吧,我知道你要说什么,用一个A*空指针访问就行,这个的确可以,但是还有没有更简单的办法?那就用静态成员函数

静态成员函数的形参表里不存在this指针,所以它可以脱离对象调用,不过它就不能访问那些直接存在对象里的内容了,哪怕函数是用对象调用的。

一般情景下,静态成员变量与静态成员函数同时存在。有用到静态变量最好就配一个静态函数

为了检验各位对静态成员的理解,我来出一道题目

请问,在不使用循环语句,选择语句,位运算,乘除法的情况下,如何输出1 + 2 + 3 + ... + n的结果(禁止使用数学相关类逃课)

出处:牛客网 JZ64(牛客网的要求没我这么严格,还能用位运算)

原理是这样的,我定义了一个长度为n的Sum数组,就可以自动调用Sum构造函数n次,我再用静态成员变量保存每次的计算数据,一个循环就做好了,接下来再用静态函数返回答案,大功告成

6. 友元

1. 友元函数

我们最早讲友元函数是为了解决流输入流输出对象与this指针抢占成员函数第一个参数的问题。友元函数的特性如下

1. 友元函数由友好声明,函数声明,函数定义三部分组成

友好声明用于表达这个函数与那些类有友好关系(友好声明在友好类(类里有友元声明的)内部,格式为friend+函数声明),函数声明用于告知编译器这个函数的确存在,函数定义则是函数实际实现之处,三者缺一不可

2. 友元函数可以访问其友好类的私有及保护成员,但是友元函数不是友好类的成员函数

3. 友元函数的友好声明不受友好类访问限定符限制,因此其位置任意

4. 友元函数的调用规则与普通函数一致

2. 友元类

友元类的声明与友元函数类似,为friend+class+类名,同样可定义在任一位置,友元类的特性如下

1. 友元关系是单向的

现在类A与类B互为友元类,两个print函数也可以正常使用,但如果我去掉类A里的友元类声明就会出现这样的错误

所以友元关系是这样的,类A说类B是它的朋友,类B才能用类A的东西,但是如果类B说类A不是它的朋友,哪怕类A说类B是它的朋友,类A还是不能用类B的东西

2. 友元关系不能传递

现实里也是这样的,你朋友的朋友不一定就是你的朋友

3. 友元关系不能继承(后面讲继承再说)

友元是一个不太好的语法,会提高代码耦合度(起码比goto强,实际场景下建议少用,尽量采用get,set函数(除非你想偷懒

7. 内部类

顾名思义,定义在类内部的类,特性如下

1. 内部类受外部类类域与访问限定符的辖制

2. 外部类(内部类外一层的)天生是内部类的友元类,但内部类天生不是外部类的友元类

这么写代码会直接报错

3. 除上述关系,两个类相互独立存在

这段代码的运行结果就是

main函数(修改后)

这是调试的结果

你也看见了,无论是类的大小,还是对象里的成员变量数都能证明A与B两个类保持着一定程度的独立关系

可喜可贺,类与对象的初级内容我们终于讲完了,下一个专栏我会讲一些附加内容,涉及到编译器对类的一些优化,难度较高只做了解,而且大概率讲的不好或者是不深,请酌情观看。下下个专栏讲内存管理,敬请期待

C++自制心得——类与对象下的评论 (共 条)

分享到微博请遵守国家法律