本文共 2041 字,大约阅读时间需要 6 分钟。
std::string *stringArray = new std::string[100]; 一切似乎都按部就班, new 语句与 delete 相匹配。然而,这却是十分错误的。这段程序将出现无法预知的行为。最起码的是,由于该 stringArray 所指向的 100 个 string 对象中的 99 个没有被析构函数所析构,它们将很有可能得不到销毁。 当使用了一个 new 语句时(也可以说,使用 new 动态创建了一个对象),将会发生两件事情。第一,分配内存(通过一个名为 operator new 的函数)。第二,为这段内存调用一个或多个构造函数。当使用了一个 delete 语句时,将会发生另外两件事情:第一,为分配的内存调用一个或多个析构函数。第二,释放内存(通过 operator delete 函数实现)。 delete 的关键问题是:内存中存在多少需要删除的对象呢?答案取决于需要调用多少析构函数。 实际上,答案十分简单,那就是:指针是指向一个单独的对象,还是一组对象?这个问题很关键,因为为单个对象分配的内存与为一系列对象分配的内存在形式上有本质的不同。具体地说,为数组分配的内存通常要保存数组的大小,这就使得 delete 很容易知道需要调用多少次析构函数。为单个对象分配的内存则不保存这一信息。 当对一个指针使用 delete 时,如何让 delete 知道这一指针是否存在数组信息呢?这里只有一种方法,那就是亲自告诉它。如果在 delete 与指针名之间添加一对中括号,则 delete 便认为这一指针指向一个数组。否则将以单一对象处理。 std::string *stringPtr1 = new std::string; std::string *stringPtr2 = new std::string[100]; delete stringPtr1; // 删除一个对象 delete [] stringPtr2; // 删除一个对象数组 如果为 stringPtr1 使用“ [] ”时将会发生什么呢?我们说,这样做不会得到预期的效果。假设使用上面的内存分配形式, delete 将会读入一些内存信息,并且将其理解为数组的长度,然后便开始调用这么多的析构函数,此时 delete 不仅忽视了它正在操作的内存上保存的并不是数组,同时它“辛辛苦苦”析构的东西很有可能都不是它所能操作的类型。 如果不为 stringPtr2 使用“ [] ”将会发生什么呢?我们也可以说,这样做同样的不到预期的效果。可以看到由于它没有调用足够的析构函数,于是将造成内存泄漏。同时,对于内建数据类型,诸如 int 等,尽管它们没有析构函数,但同样也会带来无法预期的结果(有时是有害的)。 这里的规则很简单:如果在一个 new 语句中使用了 [] ,那么必须在相关的 delete 语句中也使用 [] 。反之亦然。 有时候我们会编写这样的类:它们包含用来动态分配内存的指针,并且提供多个构造函数。此时需要时刻注意遵守上面的规则。在所有的构造函数中,当编写初始化指针成员的语句时,必须使用 new 的一致的格式。如果不这样做,那么怎么能知道析构函数中 delete 需要用什么样的格式呢? 如果倾向于使用 typedef ,那么这一规则同样值得注意,因为它意味着 typedef 的创建者必须清楚:当 typedef 的类型中使用了 new 来创建对象,那么相应的 delete 语句中必须要使用同样的格式。请看下边的示例: typedef std::string AddressLines[4]; 由于 AddressLines 是一个数组, 如果这样使用了 new : std::string *pal = new AddressLines; // 请注意“ new AddressLines ” // 返回一个 string* , 与“ new string[4] ”完全一样 delete pal; // 将出现无法预知的行为! 为了避免此类混淆,请谨慎使用 typedef 来定义数组。这十分简单,因为标准 C++ 库中包含了 string 和 vector ,使用这些模板可以摆脱动态分配数组的烦恼。比如说,在这里, AddressLines 可以定义为一个字符串的向量,也就是 vector<string> 类型。 如果在一个 new 语句中使用了 [] ,那么必须要在相关的 delete 语句中使用 [] 。如果在 new 语句中没有使用 [] ,那么在相关的 delete 语句中一定不要出现 [] 。 转载地址:http://iuznb.baihongyu.com/