G14.new && malloc()
new是一个关键字,malloc是一个函数
构造函数/析构函数:与 malloc(sizeof(Fred))不一样,new Fred() 调用 Fred 的构造函数。同样,delete p 调用 *p 的析构函数。
类型安全:malloc() 返回一个没有类型安全的 void* 。new Fred() 返回一个正确类型(一个 Fred*)的指针。
可覆盖性:new 是一个可被类重写/覆盖的算符(operator),而 malloc() 在类上没有可覆盖性。
G15.new,free(),delete,malloc()
在一个程序中同时使用 malloc() 和 delete 或者同时使用 new 和 free() 是合情合理合法的。但是,对由 new 分配的指针调用 free(),或对由 malloc() 分配的指针调用 delete,是无理的、非法的。
不要在同一个指针上混合malloc() 和 delete,或在同一个指针上混合new 和 free()。如果通过p = new char[n]分配,则必须使用delete[] p;不可以使用free(p)。如果通过分配p = malloc(n),则必须使用free(p);不可以使用delete[] p 或 delete p!
G16.new的NULL检查?
不!在 C++中,如果运行时系统无法为p = new Fred()分配 sizeof(Fred) 字节的内存,会抛出一个std::bad_alloc异常(set_new_handler()总结)。与 malloc()不同,new 永远不会返回 NULL!
因此你只要简单地写:
Fred* p = new Fred(); // 不需要检查 p 是否为 NULL
G17.delete的NULL检查?
不需要!
C++担保,如果p等于NULL,则delete p不作任何事情,因此你不应该加上多余的 if 测试。
错误的:
if (p != NULL)
delete p;
正确的:
delete p;
G18.new过程
new Fred()是一个两步的过程:分配内存,然后调用构造函数
使用void* operator new(size_t nbytes)函数分配sizeof(Fred) 字节的内存。该函数类似于malloc(size_t nbytes)。
它通过调用Fred构造函数在内存中建立对象。第一步返回的指针被作为 this 参数传递给构造函数。这一步被包裹在一个块中以处理这步中抛出异常的情况。
因此实际产生的代码可能是这样的:
//原始代码:Fred* p = new Fred();
Just like:
Fred* p = (Fred*) operator new(sizeof(Fred));
try {
new(p) Fred(); // Placement new
} catch (…) {
operator delete(p); // 释放内存
throw; // 重新抛出异常
}
可以看到,如果异常发生在p = new Fred()的 Fred 构造函数中, C++语言将进行相关处理,确保已分配的 sizeof(Fred)字节的内存会自动从堆中回收。
G19.delete过程
delete p 是一个两步的过程:调用析构函数,然后释放内存。 delete p产生的代码看上去是这样的(假设是Fred*类型的):
// 原始代码:delete p;
Just like:
if (p != NULL) {
p->~Fred();
operator delete(p);
}
p->~Fred() 语句调用 p 指向的Fred 对象的析构函数。
operator delete(p) 语句调用内存释放函数void operator delete(void* p)。该函数类似free(void* p)。
G20.继承和组合
组合:简单地创建一个包含已存在的类对象的新类,这称为组合,因为这个新类是由已存在类的对象组合的。对于新类的public接口函数,包含对嵌入对象的使用,但不必模仿这个嵌入对象的接口。
继承:创建一个新类作为一个已存在类的类型,采取这个已存在类的形式,对它增加代码,但不修改它。
组合通常在希望新类内部有已存在类性能时使用,而不希望已存在类作为它的接口。这就是说,嵌入一个计划用于实现新类性能的对象,而新类的用户看到的是新定义的接口而不是来自老类的接口。
因为正在由一个已存在的类做一个新类,并且希望这个新类与已存在的类有严格相同的接口(希望增加任何我们想要加入的其他成员函数),所以能在已经用过这个已存在类的任何地方使用这个新类,这就是必须使用继承的地方。