C++ 学习笔记之(19) new、delete表达式、RTTI(运行时类型识别)、枚举、类成员指针、嵌套类、局部类、位域、volatile、extern C
控制内存分配
重载new
和delete
new
表达式原理string *sp = new string("a value"); // 分配并初始化一个 string 对象 string *arr = new string[10]; // 分配 10 个默认初始化的 string 对象
- 调用标准库函数
operator new
或operator new[]
,该函数分配足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组) - 编译器运行相应的构造函数构造对象,并传入初始值
- 对象被分配空间并构造完成,返回一个指向该对象的指针
- 调用标准库函数
delete
表达式原理delete sp; // 销毁 *sp, 然后释放 sp 指向的内存空间 delete [] arr; // 销毁数组中的元素,然后释放对应的内存空间
- 对动态分配的对象或数组中的而元素执行相应的析构函数
- 编译器调用标准库函数
operator delete
(或operator delete[]
)释放内存空间
标准库定义
oeprator new
函数和operator delete
函数的8
个重载版本,前四个可能抛出bad_alloc
异常
- 自定义版本必须位于全局作用域或类作用域中
- 上述运算符函数定义为类的成员时,是隐式静态的。因为
new
用在对象构造之前,delete
用在对象销毁之后,故必须是静态的,且不能操纵类的任何数据成员 new
和new[]
的返回类型必须是void *
, 第一个形参类型必须是size_t
, 且该形参不能含有默认实参,但可提供额外形参。函数形式void *operator new(size_t, void*);
,不可被重载。delete
和delete[]
函数的返回类型必须是void
, 第一个形参类型必须是void*
。用指向待释放内存的指针来初始化void*
形参- 实际上自定义的是
opearot new
函数,并非new
表达式。new
表达式无法改变,自定义operator new
和operator delete
函数的目的在于改变内存分配的方式。
- 实际上自定义的是
定位new
表达式
operator new
函数分配的内存空间应该使用new
的 定位new(placement new)形式构造对象
place_address
为指针,initializers
为初始值列表,用于构造新分配的对象- 当只传入一个指针类型的实参时,定位
new
表达式构造对象但是不分配内存。定位new
表达式使用operator new(size_t, void*)
函数,该函数不分配内存,仅返回指针实参。然后由new
表达式负责在指定的地址初始化对象 - 传给
allocator
的construct
的指针必须指向同一个allocator
对象分配的孔家,但是传给定位new
的指针无需指向operator new
分配的内存,甚至不需要指向动态内存 - 与
allocator
的destroy
类似,调用析构函数会销毁对象,但是不会释放内存
运行时类型识别
运行时类型识别(run-time type identification RTTI)由连个运算符实现
typeid
运算符,用于返回表达式的类型dynamic_cast
运算符,用于将基类的指针或引用安全地转换成派生类的指针或引用- 使用情况:使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数。
- 使用
RTTI
时,最好定义虚函数而非直接操作类型管理
dynamic_cast
运算符
- dynamic_cast运算符使用形式(其中
type
必须是类类型, 且通常含有虚函数)
dynamic_cast<type*>(e)
:e
必须是一个有效的指针dynamic_cast<type&>(e)
:e
必须是一个左值dynamic_cast<type&&>(e)
:e
不能是左值
e
的类型必须符合以下三个条件中的任意一个,转换才能成功
e
的类型是目标type
的公有派生类e
的类型是目标type
的公有基类e
的类型就是目标type
的类型
dynamic_cast
转换失败情况
- 若转换目标是指针类型:失败结果为
0
- 若转换目标是引用类型:失败抛出
bad_cast
异常
- 若转换目标是指针类型:失败结果为
// 指针类型的 dynamic_cast
// bp 指针指向基类 Base(至少含有一个虚函数), dp 指针指向公有派生类 Derived
if(Derived *dp = dynamic_cast<Derived*>(bp))
{
// 使用 dp 指向的 Derived 对象
}else { // bp 指向一个 Base 对象
// 使用 bp 指向的 Base 对象
}
// 引用类型的 dynamic_cast, 因为不存在控引用,故使用异常捕获
void f(const Base &b)
{
try{
const Derived &d = dynamic_cast<const Derived&>(b);
// 使用 b 引用的 Derived 对象
}catch(bad_cast){
// 处理类型转换失败的情况
}
}
typeid
运算符
typeid(e)
:e
为任意表达式或类型的名字, 结果为一个常量对象的引用, 该对象类型是