重载new和delete
1调用operator new( 或new[])标准库函数分配足够大的、原始的、未命名的内存空间以便存储特定类型的对象
2编译器运行相应地构造函数以构造这些对象,并为其传入初始值
3返回一个指向该对象的指针
可以在全局作用域定义operator new,也可以定义为成员函数
如果是类类型,首先在本类及其基类中查找,否则在全局域中查找,最后使用标准库定义版本。
//这些版本可能抛出异常
void *operator new(size_t);
void *operator new[] (size_t);
void operator delete(void) noexcept;
void operator delete[] (void) noexcept;
//这些版本承诺不抛出异常
void *operator new(size_t, nothrow_t&) noexcept;
void *operator new[] (size_t, nothrow_t&) noexcept;
void operator delete(void, nothrow_t&) noexcept;
void operator delete[] (void, nothrow_t&) noexcept;
nothrow_t是定义在new头文件的一个struct,不包含任何成员。
int *p=new (nothrow) int;//(这种形式的new就叫定位new,提供额外参数)
当定义成类的成员时,是隐式静态的。
自定义operator new可以提供额外的参数。此时用到这些自定义函数的new表达式必须使用new的定位形式,将实参传给新增的形参。
void* operator new(size_t, void)形式不允许重载,其他都可以。
operator delete返回类型必须void,第一个形参必须void*。如果是定义为类的成员,还可以包含另外一个size_t的形参。
重载不能改变new和delete运算符的基本含义。
operator new和delete必须以某种方式执行分配内存与释放内存的操作。
定位new
new(place_address) type
new(place_address) type (initializers)
new(place_address) type [size]
new(place_address) type [size] {braced initializer list}
place_address必须是指针,定位new调用void* operator new(size_t, void*)这是一个我们无法自定义的operator new版本
该函数不分配任何内存,它只是简单地返回指针实参;然后由new表达式负责在指定地址初始化对象。允许我们在一个特定的、预先分配的内存地址上构造对象。
定位new与allocator区别
传给construct的指针必须指向同一个allocator对象分配的空间
但是传给定位new的指针无须是operator new分配的内存
RTTI
typeid运算符,返回表达式的类型
dynamic_cast运算符,将基类指针或引用安全地转换成派生类的指针或引用
特别适用于我们想使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数。
潜在风险:必须清楚地知道转换的目标类型,并且必须检查类型转换是否被成功执行。
dynamic_cast<type*> (e);
dynamic_cast<type&> (e);//e必须左值
dynamic_cast<type&&> (e);//e不能左值
指针类型转换失败,返回0;引用类型转换失败,抛出一个bad_cast
if(Derived dp=dynamic_cast<Derived> (bp))
{}
else
{}
在条件语句中执行操作,可以确保类型转换和结果检查在同一条表达式中完成。dp在外部不可访问,即时忘了做相应判断,也确保安全。
typeid
作用域对象,结果是一个常量对象的引用。
顶层const忽略;如果是引用返回引用所引对象的类型;数组或函数不做指针转换
typeid是否需要运行时检查决定了表达式是否会被求值。
虚函数必须具有相同的形参,所以只能使用基类的成员,而不能使用派生类独有的。
使用typeid判断,再使用dynamic_cast转换
type_info没有默认构造,拷贝移动构造及赋值运算都定义为删除的。创建对象的唯一途径是使用typeid运算符。
其他
- 限定作用域的枚举类型,遵循常规作用域准则
int I = color::red;//不限定作用域的枚举隐式地转换成整型
Int j= peppers::red;//限定作用域的枚举不会进行隐式转换
- 前置声明
不限定作用域的必须制定成员类型,限定作用域的可以使用默认的int
enum intValues : unsigned long long;
enum class open_modes;
- 类成员指针
指向类的非静态成员的指针,并没有指向任何数据,只有当解引用时才提供对象的信息。
成员函数与指向成员函数的指针之间不存在自动转换规则。
可以使用类型别名,做成成员指针函数表,使代码更易读写
用作可调用对象
1function<bool (const string&)> fcn =&string::empty;
find_if(svec.begin(), sevc.end(), fcn);
function<bool (const string)> fcn =&string::empty;
find_if(pvec.begin(), pevc.end(), fcn);
使用function为成员函数生成一个可调用对象必须先翻译代码,使隐式形参变成显示
可以理解为类的成员函数有两个重载版本,需要确定用哪一个
2mem_fn
auto f =mem_fn(&string::empty);
f(
f(&svec[0]);
含有一对重载的函数调用运算符
3bind
auto it =find_if(svec.begin(), sevc.end(), bind(&string::empty, _1);
- 嵌套类
嵌套类的名字在外层类作用域中是可见的,外层类作用域之外不可见
嵌套类必须声明在类的内部,但是可以定义在类的内部或外部
如声明了静态成员,定义需在外层类的作用域外
不在类的作用域内需要加前缀
- union
union不能含有引用类型的成员,默认公有
可以定义成员函数,但不能继承自其它类也不能作为基类,不能含有虚函数
匿名union,编译器自动为该union自动创建一个未命名对象。定义所在作用域内可以直接访问其成员。
对于union,构造和销毁类类型成员必须执行复杂的操作,通常使用类来管理
- 局部类
定义在某个函数内部,所有成员必须完整定义在类的内部,不能有静态数据成员。
只能访问外层作用域定义的类型名、静态变量以及枚举类型
- 不可移植特性
位域,成员名字之后紧跟冒号以及一个常量表达式,最好设为无符号类型
volatile跟const用法类似,只有volatile成员函数才能被volatile对象调用
区别,不能使用合成的拷贝移动构造函数及赋值运算初始化化volatile对象或从volatile对象赋值。合成的成员接受的形参类型是(非volatile常量引用)
链接指示 extern “C”
需要调用其他语言编写的函数,也必须在C++中进行声明,且必须指定返回类型和形参列表
可单个可复合
extern “C”
{
int strcmp(const char*, const char*);
}
多重声明可以应用于整个头文件,链接指示可以嵌套
语言是函数类型的一部分,C函数的指针与C++函数的指针是不一样的类型
extern “C” void f1(void(*)(int));
链接指示对整个声明都有效,包括形参。
想在C++函数中传入C函数的指针,必须使用类型别名
C和C++中编译同一个源文件,可以在编译C++版本的程序时预处理器定义__cplusplus(两个下划线),条件编译
#ifdef __cplusplus
extern “C”
#endif
int strcmp(const char*, const char*);