malloc 和free:他们不知道构造函数和析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内。
假设用两种方法给一个包含 10 个string 对象的数组分配空间,一个用
malloc,另一个用new:
string *stringArray1 = static_cast<string*>(malloc(10 * sizeof(string)));
string *stringArray2 = new string[10];
其结果是,stringArray1确实指向的是可以容纳10 个string 对象的足够空
间,但内存里并没有创建这些对象。而且,如果你不从这种晦涩的语法怪圈
里跳出来的话,你没有办法来初始化数组里的对象。
换句话说,stringArray1 其实一点用也没有。相反,stringArray2 指向的是一个
包含10 个完全构造好的string 对象的数组,每个对象可以在任何读取string 的
操作里安全使用。
假设你想了个怪招对stringArray1 数组里的对象进行了初始化,那么在你
后面的程序里你一定会这么做:
free(stringArray1);
delete [] stringArray2;
调用free 将会释放stringArray1 指向的内存,但内存里的string 对象不会调用
析构函数。如果string 对象象一般情况那样,自己已经分配了内存,那这些内
存将会全部丢失。相反,当对stringArray2 调用delete 时,数组里的每个对象
都会在内存释放前调用析构函数。
既然 new 和delete 可以这么有效地与构造函数和析构函数交互,选用它们
是显然的。
把 new 和delete 与malloc 和free 混在一起用也是个坏想法。对一个用new
获取来的指针调用free,或者对一个用malloc 获取来的指针调用delete,其后
果是不可预测的。大家都知道“不可预测”的意思:它可能在开发阶段工作良
好,在测试阶段工作良好,但也可能会最后在你最重要的客户的脸上爆炸。
new/delete 和malloc/free 的不兼容性常常会导致一些严重的复杂性问题。
举个例子,<string.h>里通常有个strdup 函数,它得到一个char*字符串然后返
回其拷贝:
char * strdup(const char *ps); // 返回ps 所指的拷贝
在有些地方,C 和C++用的是同一个strdup 版本,所以函数内部是用malloc
分配内存。这样的话,一些不知情的C++程序员会在调用strdup 后忽视了必须
对strdup 返回的指针进行free 操作。为了防止这一情况,有些地方会专门为C++
重写strdup,并在函数内部调用了new,这就要求其调用者记得最后用delete。
你可以想象,这会导致多么严重的移植性问题,因为代码中strdup 以不同的形
式在不同的地方之间颠来倒去。
C++程序员和C 程序员一样对代码重用十分感兴趣。大家都知道,有大量
基于malloc 和free 写成的代码构成的C 库都非常值得重用。在利用这些库时,
最好是你不用负责去free 掉由库自己malloc 的内存,并且/或者,你不用去malloc
库自己会free 掉的内存,这样就太好了。其实,在C++程序里使用malloc 和free
没有错,只要保证用malloc 得到的指针用free,或者用new 得到的指针最后
用delete 来操作就可以了。千万别马虎地把new 和free 或malloc 和delete 混
起来用,那只会自找麻烦。
既然 malloc 和_______free 对构造函数和析构函数一无所知,把malloc/free 和
new/delete 混起来用又象嘈杂拥挤的晚会那样难以控制,那么,你最好就什么
时候都一心一意地使用new 和delete 吧