从项目中的bug来源说起
数据/逻辑关系不明确
- 数据操作关系混乱
数据初始化与回收
- 初始化称为程序异常的一个来源
- 资源回收也成为程序资源管理的缺陷
数据访问修改权限不受控
- 全局变量,多模块随意访问
- 静态全局,编译单元内随意访问
- 数据访问接口随意定义(多接口访问相同数据)
代码复用度灵活度不够
- 数据复用
- 行为复用(函数/接口调用无法复用)
所以需要三个特性
封装
- 抽象功能,代码模块化,隐藏实现细节。
- 访问控制,安全与灵活的平衡。
继承
- 基于代码重用来扩展功能(代码重用,静态)。
- 继承和组合的区别
多态
- 基于接口重用来提高灵活性(接口重用,动态)
封装之类和对象
类里有什么?
- 非虚成员函数
- 虚成员函数
- 虚函数表(vftable)
- 虚基类表(vbtable)
实例里有什么?
- 成员变量
- 虚函数指针(vfptr)指向虚函数表。
- 徐基类指针(vbptr)指向虚基类表。
静态数据区
- 静态成员
- 虚函数表
- 徐基类表
代码区
- 虚函数
- 非虚函数
构造和析构
三巨头 (Big-Three)
构造函数
析构函数
赋值函数
四个函数
A() // 默认构造函数
A(const A&) // 默认拷贝构造函数
~A() // 析构函数
A& operator = (const A&a) // 默认赋值函数
构造析构顺序
在类中声明的顺序进行,和初始化列表中的顺序无关。
派生类的够构造顺序
先基类,后成员。
析构的顺序
和构造相反的顺序进行。
构造和析构的调用时机
非静态局部变量
- 程序执行到对象的定义时创建。
- 如果没有初始值,则调用默认的构造函数。
- 如果没有默认构造函数,则不初始化。
- 程序执行到对象生存域结尾时,暗中调用析构函数。
静态局部变量
- 程序执行到对象的定义时创建。
- 如果没有初始值,则调用默认的构造函数。
- 如果没有默认构造函数,则自动初始化为0。
- 直到main结束后才会调用析构函数。
全局对象
- 程序进入main之前采用定义时的初始值。
- 如果没有初始值,则调用默认的构造函数。
- 如果没有默认构造函数,则初始化为0。
- 直到main结束后才会调用析构函数。
动态创建对象
- 调用new运算符创建对象时调用构造函数初始化。
- 如果接收返回地址的指针为静态局部指针变量,等同静态局部对象的情况
- 如果接收返回地址的指针为非静态局部指针变量,等同非静态局部对象的情况。
- 调用delete运算符删除对象时,自动调用对象的析构函数。
(续)