封装
将具体实现过程和数据封装成一个类,只能通过接口进行访问。
作用:保护或防止代码在无意之中被破坏,保护类中的成员。
继承
子类继承父类的特征和行为,复用了基类的全体数据和成员函数(基类私有成员可被继承,但是无法被访问),其中构造函数、析构函数、友元函数、静态数据成员、静态成员函数都不能被继承。
final 标识符
struct test final {};
放在类的后面表示该类无法被继承,也就是阻止了从类的继承;
放在虚函数后面该虚函数无法被重写,表示阻止虚函数的重载。
多态
不同继承类的对象对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现形式。
多态原理
- 动态多态:利用虚函数实现运行时的多态,利用虚函数表调用对应的表。
- 静态多态:通过函数重载来实现。
虚函数
虚函数是在基类中使用关键字 virtual
声明的成员函数,它允许派生类对其进行重写(Override),实现运行时多态。
当通过基类指针或引用调用虚函数时,实际调用的是对象类型对应的派生类中的函数,这个过程称为动态绑定(Dynamic Binding)。
final 标识符
struct test final {
virtual void mk() final {};
};
放在类的后面表示该类无法被继承,也就是阻止了从类的继承;
放在虚函数后面该虚函数无法被重写,表示阻止虚函数的重载。
override 标识符
class basic {
virtual void mk() {};
};
struct test : basic {
virtual void mk() override {};
};
保证在派生类中声明的重载函数,与基类的虚函数有相同的签名。
虚函数原理
类的虚函数表:
- 当一个类中包含虚函数时,编译器会为该类生成虚函数表,表中保存该类包含的虚函数的地址。
- 一个包含虚函数的类,至少有1张虚函数表。
- 一个普通类继承了n个有虚函数的基类,就有n张虚函数表。
- 类中重写父类的虚函数A,那自己虚函数表中将A对应的地方,替换成重写后的虚函数的地址。
对象的虚函数表指针: - 当一个类中包含虚函数时,该类的对象将会拥有虚函数表指针,指向该类的虚函数表。
- 类有n张虚函数表,类的对象就有n个虚指针,每个指针指向1张虚函数表。
对象 $\stackrel{虚函数表指针}{\rightarrow}$ 虚函数表 $\stackrel{虚函数指针}{\rightarrow}$ 虚函数。
- 虚函数表:在编译时生成,存储在只读数据段。
- 虚函数表指针:在对象创建时生成,位置在对象的头部,根据对象创建方式存储在堆或栈上。
纯虚函数
class A {
virtual void example() = 0; // 纯虚函数
}
纯虚函数是虚函数的一种特殊形式,它的语法是在函数声明后加上’=0’。纯虚函数只有声明没有实现,含有纯虚函数的类称为抽象类,不能被实例化。它的派生类如果想被实例化,就必须实现所有的纯虚函数。
哪些函数不能是虚函数?
- 构造函数:执行构造函数前虚表指针尚未初始化,无法正确调用构造函数(析构函数可以是虚函数)。
- 静态函数:静态函数不属于对象属于类,静态成员函数没有this指针,因此静态函数设置为虚函数没有任何意义。
- 友元函数,友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
- 普通函数,普通函数不属于类的成员函数,不具有继承特性,因此普通函数没有虚函数。
智能指针
auto_ptr
已弃用。
unique_ptr
std::unique_ptr
是独占所有权的智能指针,一个对象只能被一个 std::unique_ptr
管理,不能共享所有权。
主要用途:
- 自动释放资源:当
std::unique_ptr
超出作用域时,会自动释放所管理的资源。 - 独占所有权:确保同一资源不会被多个指针操作,避免潜在的内存问题。
- 高效传递所有权:通过
std::move
可以将资源所有权转移。
实现要点:
- 成员变量:包含一个裸指针
T* ptr
和可定制的删除器。 - 禁用拷贝:通过
= delete
或私有化拷贝构造函数和赋值运算符,禁止复制语义。 - 支持移动:移动构造函数和赋值操作符转移所有权,原指针置
nullptr
。 - 析构释放:析构时调用删除器(默认
delete ptr
)释放资源。
shared_ptr
std::shared_ptr
是具有共享所有权的智能指针,多个 std::shared_ptr
可以同时管理同一资源,采⽤引用计数器,当计数为0的时候会自动的释放动态分配的资源。
主要用途:
- 共享资源的生命周期管理:适用于多个对象需要访问和管理同一资源的场景。
- 引用计数:内部维护一个引用计数,记录资源被引用的次数。
- 线程安全:引用计数是线程安全的,多线程读写不安全。
实现要点:
- 控制块:包含强引用计数(
use_count
)、弱引用计数(weak_count
)、原始指针和删除器。 - 原子操作:引用计数的增减是线程安全的(如
std::atomic
)。 - 构造/拷贝:新实例递增强引用计数;析构时递减,计数归零则释放资源。
- 循环引用解决:需结合
weak_ptr
打破循环。
weak_ptr
std::weak_ptr
是一种弱引用的智能指针,它不会增加资源的引用计数,通常与 std::shared_ptr
配合使用。
主要用途:
- 解决循环引用问题:避免
std::shared_ptr
之间的循环引用导致资源无法释放。 - 观察资源状态:可以安全地观察
std::shared_ptr
管理的资源是否仍然存在。
实现要点:
- 依赖控制块:与
shared_ptr
共享同一控制块,但仅操作弱引用计数。 - 提升为
shared_ptr
:通过lock()
方法检查强引用计数,若有效则创建新shared_ptr
。 - 控制块生命周期:强引用计数归零时释放对象,弱引用计数归零时释放控制块。
左值
可以获取它的地址。
可以修改(非 const
)。
右值
右值不能取地址。
不能修改。
引用
引用是对被引用的对象取一个别名。
右值引用
右值引用是为一个临时变量取别名,它只能绑定到一个临时变量或表达式(将亡值)上。
支持移动语义,右值引用可以绑定到临时对象、表达式等右值上,这些右值在生命周期结束后就会被销毁,因此可以在右值引用中窃取其资源,从而避免昂贵的复制操作,实现高效的移动语义。
const
- 修饰变量:该变量不能被改变。
- 修饰成员函数:函数不能改变对象的值。
- 修饰指针:const 修饰指针指向的内容,则内容为不可变量,const 修饰指针,则指针为不可变量。
new / malloc
- new 自动申请大小合适的内存,返回的是对象类型的指针,类型严格与对象匹配,申请失败抛出异常。
- malloc 手动指定内存大小,返回 void*,需要类型转换,申请失败返回 NULL。
Comments NOTHING