内存管理
内存管理的层级以及对应的特性如下:
1 | void *p1 = malloc(512); //512 bytes |
执行一条delete表达式时,实际上执行了如下两个步骤:
执行指针所指向对象的析构函数
编译器调用operator delete或operator delete[]
的标准库函数释放内存空间
如果应用程序希望控制内存的分配过程,则需要自己定义operator new函数和operator delete函数。如果我们定义了自己的版本,则编译器将使用我们自定义版本替换标准库定义的版本。
new以及delete对应的具体细节:
1 | try |
placement new允许我们将对象构建于已分配的内存中,没有所谓的placement delete,因为placement new根本没有分配内存。
类作域中重载operator new和operator delete
当我们将重载的operator new和operator delete定义为类的成员时,它们是隐式静态的。我们无须显式地声明static,但显式声明也不会引发错误。因为operator new用在对象构造之前,而operator delete用在对象销毁之后,所以这两个成员必须是静态的,而且它们不能操纵类的任何数据成员。
对于operator new或者operator new[]函数来说,它的返回类型必须是void*
,第一个参数的类型必须是size_t且该形参不能含有默认实参。
对于operator delete或operator delete[]函数,它们的返回类型必须是void,第一个形参类型必须是void*
。
与析构函数类似,operator delete也不允许抛出异常,必须使用noexcept异常说明符指定其不抛出异常。
当将operator delete定义成类的成员时,该函数还可以包含一个额外的形参,形参的初始值是第一个形参所指对象的字节数。
自定义的operator new和operator delete函数的目的在于改变内存的分配方式,但不管怎么样,我们都不能改变new运算符和delete运算符的基本含义。
1 |
|
全局作域中重载operator new和operator delete
我们也可以在全局作用域中重载operator new和opeator delete函数,但在全局作用中重载将导致C++标准库,new、delete表达式都将使用全局重载的operator new和operator delete函数,其影响无远弗届。
1 |
|
运行时类型识别
dynamic_cast运算符
与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。
所以,dynamic_cast的使用形式如下:
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
其中,type必须是一个类类型,并且通常情况下该类型应该包含虚函数。如果类型中没有虚函数,则编译报错:source type is not polymorphic
,可能是因为当类没有虚函数表的时候,dynamic_cast就不能用RTTI来确定类的具体类型
对于从子类到基类的指针转换,static_cast和dynamic_cast都是成功并且正确的(所谓成功是说转换没有编译错误或者运行异常;所谓正确是指方法的调用和数据的访问输出是期望的结果),这是面向对象多态性的完美体现。
而从基类到子类的转换,static_cast和dynamic_cast都是成功的,但是正确性方面:dynamic_cast的结果是空指针,而static_cast则是非空指针。但很显然,static_cast的结果应该算是错误的,
对于没有关系的两个类之间的转换,输出结果表明,dynamic_cast依然是返回一个空指针以表示转换是不成立的;static_cast直接在编译期就拒绝了这种转换。
1 |
|