2020-C++高级程序设计-C++ 类的封装
C++ 类的封装
1. 类
- 两种成员:
- 数据
- 操作(函数)
- 将实现和类定义分离
- 头文件主要是声明
- 源文件:存储实现
- 在链接的时候,将其他文件中的部分连接过来。
1 |
|
- 如果直接将函数定义直接放在头文件里,会建议compiler将其作为inline函数进行编译。
- 如果函数长度很长的话,反复调用的函数调用时间就会占比很小,而相反的话则会很大。
- 随便使用内联函数可能是的代码很烂:get和set函数我们选择使用inline方式
- 代码长度不超过10行,不包含for、switch等语句。
1 |
|
2. 类的构造函数
- 对象的初始化(完成对象内存分配)
- 为创建的对象建立标识符
- 为对象数据成员开辟内存空间
- 按照规定对成员变量进行初始化
- 描述
- 与类同名,无返回类型(不是void)
- 自动调用,不可直接调用
- 可重载
- 默认构造函数:无参数
- 当类中未提供构造函数时,编译系统提供默认构造函数。
- 为什么要有?对于类的成员变量,默认值初始化
- 如果你写一个带参数,那么你必须要自己配一个没有参数的默认构造函数。
- public:可定义为private:接管对象创建
- private的构造函数:单例模式,类内部的构造方法控制(可以控制类的个数)
- 调用:
- 自动按照参数列表来对应构造函数
- 具体调用方式参照底下。
1 |
|
3. 成员初始化表(构造函数中变量初始化的一种方法)
- 构造函数的补充
- 构造函数:先开辟空间并赋默认值
- 成员初始化表:开辟空间的时候就赋值
- 执行:(常量和引用的声明和定义要放在一起,只能通过这个方法来完成)
- 先于构造函数执行
- 按类数据成员声明次序:下面的例子中先 x 再 y 再 z
static const
:常量数字,这个是可以在类内部进行初始化(static const a = 1;
)
1 |
|
- 减轻Compiler负担:
- 正常构造函数中赋值
x = 100
:首先对象构造的时候进行了赋值,之后再次进行了赋值,共计2次 - 成员初始化表的时候,只进行了赋值一次。
- 正常构造函数中赋值
- 初始化顺序问题:先执行p,再执行size有问题,按照字面序进行。
1 |
|
- 在构造函数中尽量使用成员初始化表取代赋值动作
- const 成员 / reference 成员 / 对象成员:为什么?,默认构造函数?
- 效率高:见上面
- 数据成员太多的时,不采用本条准则,降低可维护性
- C++ 11之后允许在构造函数外进行初始化:避免在每个函数的成员初始化表中进行初始化。
1 |
|
4. 类的析构函数
- 格式:
~<类名>()
- 功能:RAII:Resource Acquisition Is Initialization(资源获取即初始化)
- 调用情况
- 对象消亡时,系统自动调用
- C++离开作用域的时候回收
- 使用delete关键字的时候进行调用
4.1. C++资源回收机制
- Java的垃圾回收机制:finalize():调用后在下一次垃圾回收的时候才会进行回收
- 效率不好,会卡。有些不支持。
- GC的效率存在障碍,存在不能使用GC的场合
- GC只能回收Java存放在堆上的资源
- C++的垃圾回收机制:谁创造谁释放,主动权在程序员手里。稳定效率,表现好。
- Private的析构函数:(强制自主控制对象存储分配)
- 回收对象的过程被接管,保证对象在堆上进行创建,但是不能使用delete,那么我们可以在内容提供一个destroy()方法来进行回收
- 写在栈或者全局区是不能通过编译的(自动调用,发现调不到)
- 强制在堆上进行创建,对很大的对象而言有好处强制管理存储分配
- 适用于内存栈比较小的嵌入式系统
1 |
|
- 更好的解决方案声明成静态方法:free
1 |
|
- 栈上的内存资源会自动释放,所以我们只针对堆上的资源的释放
4.2. 析构函数例子
1 |
|
5. 类的拷贝构造函数
- 相同类型的类对象是通过拷贝构造函数来完成整个复制过程:自动调用:创建对象时,用一同类的对象对其初始化的时候进行调用。
- 默认拷贝构造函数
- 逐个成员初始化(member-wise initialization)
- 对于对象成员,该定义是递归的
- 什么时候需要拷贝构造函数:
- 赋值拷贝构造
- 传参进行拷贝
- 返回值进行拷贝
- 拷贝构造函数私有:目的是让编译器不能调用拷贝构造函数,防止对象按值传递,只能引用传递(对象比较大)
5.1. 拷贝函数的使用情况以及定义
1 |
|
- 为什么对象是一个引用类型:不然会出现循环拷贝问题:如果没有引用的话,传参则会拷贝,那么就会出现循环拷贝
- 按照这个格式背过。
5.2. 拷贝构造函数的深拷贝
1 |
|
- 原来S1和S2两个指针都指向"abcd",但是随着S1的归还,S2就变成了一个空指针了。
- 此时我们通过深拷贝完成拷贝
- 没有深拷贝需求的时候,使用编译器默认构造函数即可
5.3. 拷贝构造函数的初始化问题
- 包含成员对象的类
- 默认拷贝构造函数:调用成员对象的拷贝构造函数
- 自定义拷贝构造函数:调用成员对象的默认构造函数:程序员如果接管这件事情,则编译器不再负责任何默认参数。
- 拷贝函数的拷贝过程没有处理静态数据成员
- 默认拷贝构造函数:
- 逐个成员初始化
- 对于对象成员,该定义是递归的
1 |
|
- 如果想要调用A的拷贝构造函数的话:
B(const B& b):a(b.a){z = b.z;}
- 移动构造函数:将存储单元从一个对象移动到另一个对象
move constructor A(A&&)
,例子如下
1 |
|
- 移动构造:
move constructor A(A&&)
:将已经创建好的部分移动给对应部分,避免进行重复拷贝。
5.4. 拷贝构造函数的部分问题
- 拷贝构造函数必须是引用传递,不能是值传递? 防止递归调用
- 如何识别拷贝构造函数?构造函数的第一个参数是(X&|const X&|volatile X&|const volatile X&)
5.5. 参考
6. 类的移动构造函数
1 |
|
- 左值:左侧变量,右值是常数、表达式或者函数。
- Const只能被绑定到右值上
- 不可以写成
int &x = 5
- 为什么不可以对非const引用绑定一个右值?可能会导致可以修改临时变量的值,不允许被修改。
- 不可以写成
- 移动构造函数:直接将对应的右值移动过来(我们已经将vector和String进行了是此岸)
- &&是右值引用,不会被左值调用。
- 五删原则:拷贝构造、拷贝赋值、析构函数、移动构造、移动复制
- 提供上面的5个函数之一,则需要自己提供默认函数
- 书面考试不做要求
2020-C++高级程序设计-C++ 类的封装
https://spricoder.github.io/2020/07/01/2020-C-plus-plus-advanced-programming/C++-OOP/2020-C-plus-plus-advanced-programming-C++%20%E7%B1%BB%E7%9A%84%E5%B0%81%E8%A3%85/