Team LiB
Previous Section Next Section

5.11. The new and delete Expressions

5.11. newdelete 表达式

In Section 4.3.1 (p. 134) we saw how to use new and delete expressions to dynamically allocate and free arrays. We can also use new and delete to dynamically allocate and free single objects.

第 4.3.1 节介绍了如何使用 newdelete 表达式动态创建和释放数组,这两种表达式也可用于动态创建和释放单个对象。

When we define a variable, we specify a type and a name. When we dynamically allocate an object, we specify a type but do not name the object. Instead, the new expression returns a pointer to the newly allocated object; we use that pointer to access the object:

定义变量时,必须指定其数据类型和名字。而动态创建对象时,只需指定其数据类型,而不必为该对象命名。取而代之的是,new 表达式返回指向新创建对象的指针,我们通过该指针来访问此对象:

     int i;              // named, uninitialized int variable
     int *pi = new int;  // pi points to dynamically allocated,
                         // unnamed, uninitialized int

This new expression allocates one object of type int from the free store and returns the address of that object. We use that address to initialize the pointer pi.

这个 new 表达式在自由存储区中分配创建了一个整型对象,并返回此对象的地址,并用该地址初始化指针 pi

Initializing Dynamically Allocated Objects

动态创建对象的初始化

Dynamically allocated objects may be initialized, in much the same way as we initialize variables:

动态创建的对象可用初始化变量的方式实现初始化:

     int i(1024);              // value of i is 1024
     int *pi = new int(1024);  // object to which pi points is 1024
     string s(10, '9');                   // value of s is "9999999999"
     string *ps = new string(10, '9');    // *ps is "9999999999"

We must use the direct-initialization syntax (Section 2.3.3, p. 48) to initialize dynamically allocated objects. When an initializer is present, the new expression allocates the required memory and initializes that memory using the given initializer(s). In the case of pi, the newly allocated object is initialized to 1024. The object pointed to by ps is initialized to a string of 10 nines.

C++ 使用直接初始化(direct-initialization)语法规则(第 2.3.3 节)初始化动态创建的对象。如果提供了初值,new 表达式分配到所需要的内存后,用给定的初值初始化该内存空间。在本例中,pi 所指向的新创建对象将被初始化为 1024,而 ps 所指向的对象则初始化为十个9的字符串。

Default Initialization of Dynamically Allocated Objects

动态创建对象的默认初始化

If we do not explicitly state an initializer, then a dynamically allocated object is initialized in the same way as is a variable that is defined inside a function. (Section 2.3.4, p. 50) If the object is of class type, it is initialized using the default constructor for the type; if it is of built-in type, it is uninitialized.

如果不提供显式初始化,动态创建的对象与在函数内定义的变量初始化方式相同(第 2.3.4 节)。对于类类型的对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化。

     string *ps = new string; // initialized to empty string
     int *pi = new int;       // pi points to an uninitialized int

As usual, it is undefined to use the value associated with an uninitialized object in any way other than to assign a good value to it.

通常,除了对其赋值之外,对未初始化的对象所关联的值的任何使用都是没有定义的。

Just as we (almost) always initialize the objects we define as variables, it is (almost) always a good idea to initialize dynamically allocated objects.

正如我们(几乎)总是要初始化定义为变量的对象一样,在动态创建对象时,(几乎)总是对它做初始化也是一个好办法。

We can also value-initialize (Section 3.3.1, p. 92) a dynamically allocated object:

同样也可对动态创建的对象做值初始化(value-initialize)(第 3.3.1 节):

     string *ps = new string();  // initialized to empty string
     int *pi = new int();  // pi points to an int value-initialized to 0
     cls *pc = new cls();  // pc points to a value-initialized object of type cls

We indicate that we want to value-initialize the newly allocated object by following the type name by a pair of empty parentheses. The empty parentheses signal that we want initialization but are not supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized or ask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant:

以上表明程序员想通过在类型名后面使用一对内容为空的圆括号对动态创建的对象做值初始化。内容为空的圆括号表示虽然要做初始化,但实际上并未提供特定的初值。对于提供了默认构造函数的类类型(例如 string),没有必要对其对象进行值初始化:无论程序是明确地不初始化还是要求进行值初始化,都会自动调用其默认构造函数初始化该对象。而对于内置类型或没有定义默认构造函数的类型,采用不同初始化方式则有显著的差别:

     int *pi = new int;         // pi points to an uninitialized int
     int *pi = new int();       // pi points to an int value-initialized to 0

In the first case, the int is uninitialized; in the second case, the int is initialized to zero.

第一个语句的 int 型变量没有初始化,而第二个语句的 int 型变量则被初始化为0。

The () syntax for value initialization must follow a type name, not a variable. As we'll see in Section 7.4 (p. 251)

值初始化的 () 语法必须置于类型名后面,而不是变量后。正如我们将要学习的第 7.4 节的例子:

     int x(); // does not value initialize x

declares a function named x with no arguments that returns an int.

这个语句声明了一个名为 x、没有参数而且返回 int 值的函数。


Memory Exhaustion

耗尽内存

Although modern machines tend to have huge memory capacity, it is always possible that the free store will be exhausted. If the program uses all of available memory, then it is possible for a new expression to fail. If the new expression cannot acquire the requested memory, it throws an exception named bad_alloc. We'll look at how exceptions are thrown in Section 6.13 (p. 215).

尽管现代机器的内存容量越来越大,但是自由存储区总有可能被耗尽。如果程序用完了所有可用的内存,new 表达式就有可能失败。如果 new 表达式无法获取需要的内存空间,系统将抛出名为 bad_alloc 的异常。我们将在第 6.13 节介绍如何抛出异常。

Destroying Dynamically Allocated Objects

撤销动态创建的对象

When our use of the object is complete, we must explicitly return the object's memory to the free store. We do so by applying the delete expression to a pointer that addresses the object we want to release.

动态创建的对象用完后,程序员必须显式地将该对象占用的内存返回给自由存储区。C++ 提供了 delete 表达式释放指针所指向的地址空间。

     delete pi;

frees the memory associated with the int object addressed by pi.

该命令释放 pi 指向的 int 型对象所占用的内存空间。

It is illegal to apply delete to a pointer that addresses memory that was not allocated by new.

如果指针指向不是用 new 分配的内存地址,则在该指针上使用 delete 是不合法的。

The effect of deleting a pointer that addresses memory that was not allocated by new is undefined. The following are examples of safe and unsafe delete expressions:

C++ 没有明确定义如何释放指向不是用 new 分配的内存地址的指针。下面提供了一些安全的和不安全的 delete expressions 表达式。

     int i;
     int *pi = &i;
     string str = "dwarves";
     double *pd = new double(33);
     delete str; // error: str is not a dynamic object
     delete pi;  // error: pi refers to a local
     delete pd;  // ok

It is worth noting that the compiler might refuse to compile the delete of str. The compiler knows that str is not a pointer and so can detect this error at compile-time. The second error is more insidious: In general, compilers cannot tell what kind of object a pointer addresses. Most compilers will accept this code, even though it is in error.

值得注意的是:编译器可能会拒绝编译 strdelete 语句。编译器知道 str 并不是一个指针,因此会在编译时就能检查出这个错误。第二个错误则比较隐蔽:通常来说,编译器不能断定一个指针指向什么类型的对象,因此尽管这个语句是错误的,但在大部分编译器上仍能通过。

delete of a Zero-Valued Pointer

零值指针的删除

It is legal to delete a pointer whose value is zero; doing so has no effect:

如果指针的值为 0,则在其上做 delete 操作是合法的,但这样做没有任何意义:

     int *ip = 0;
     delete ip; // ok: always ok to delete a pointer that is equal to 0

The language guarantees that deleting a pointer that is equal to zero is safe.

C++ 保证:删除 0 值的指针是安全的。

Resetting the Value of a Pointer after a delete

delete 之后,重设指针的值

When we write

执行语句

     delete p;

p becomes undefined. Although p is undefined, on many machines, p still contains the address of the object to which it pointed. However, the memory to which p points was freed, so p is no longer valid.

后,p 变成没有定义。在很多机器上,尽管 p 没有定义,但仍然存放了它之前所指向对象的地址,然而 p 所指向的内存已经被释放,因此 p 不再有效。

After deleting a pointer, the pointer becomes what is referred to as a dangling pointer. A dangling pointer is one that refers to memory that once held an object but does so no longer. A dangling pointer can be the source of program errors that are difficult to detect.

删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。

Setting the pointer to 0 after the object it refers to has been deleted makes it clear that the pointer points to no object.

一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。



Dynamic Allocation and Deallocation of const Objects

const 对象的动态分配和回收

It is legal to dynamically create const objects:

C++ 允许动态创建 const 对象:

     // allocate and initialize a const object
     const int *pci = new const int(1024);

Like any const, a dynamically created const must be initialized when it is created and once initialized cannot be changed. The value returned from this new expression is a pointer to const int. Like the address of any other const object, the return from a new that allocates a const object may only be assigned to a pointer to const.

与其他常量一样,动态创建的 const 对象必须在创建时初始化,并且一经初始化,其值就不能再修改。上述 new 表达式返回指向 intconst 对象的指针。与其他 const 对象的地址一样,由于 new 返回的地址上存放的是 const 对象,因此该地址只能赋给指向 const 的指针。

A const dynamic object of a class type that defines a default constructor may be initialized implicitly:

对于类类型的 const 动态对象,如果该类提供了默认的构造函数,则此对象可隐式初始化:

     // allocate default initialized const empty string
     const string *pcs = new const string;

This new expression does not explicitly initialize the object pointed to by pcs. Instead, the object to which pcs points is implicitly initialized to the empty string. Objects of built-in type or of a class type that does not provide a default constructor must be explicitly initialized.

new 表达式没有显式初始化 pcs 所指向的对象,而是隐式地将 pcs 所指向的对象初始化为空的 string 对象。内置类型对象或未提供默认构造函数的类类型对象必须显式初始化。

Caution: Managing Dynamic Memory Is Error-Prone

警告:动态内存的管理容易出错

The following three common program errors are associated with dynamic memory allocation:

下面三种常见的程序错误都与动态内存分配相关:

  1. Failing to delete a pointer to dynamically allocated memory, thus preventing the memory from being returned to the free store. Failure to delete dynamically allocated memory is spoken of as a "memory leak." Testing for memory leaks is difficult because they often do not appear until the application is run for a test period long enough to actually exhaust memory.

    删除( delete )指向动态分配内存的指针失败,因而无法将该块内存返还给自由存储区。删除动态分配内存失败称为“内存泄漏(memory leak)”。内存泄漏很难发现,一般需等应用程序运行了一段时间后,耗尽了所有内存空间时,内存泄漏才会显露出来。

  2. Reading or writing to the object after it has been deleted. This error can sometimes be detected by setting the pointer to 0 after deleting the object to which the pointer had pointed.

    读写已删除的对象。如果删除指针所指向的对象之后,将指针置为 0 值,则比较容易检测出这类错误。

  3. Applying a delete expression to the same memory location twice. This error can happen when two pointers address the same dynamically allocated object. If delete is applied to one of the pointers, then the object's memory is returned to the free store. If we subsequently delete the second pointer, then the free store may be corrupted.

    对同一个内存空间使用两次 delete 表达式。当两个指针指向同一个动态创建的对象,删除时就会发生错误。如果在其中一个指针上做 delete 运算,将该对象的内存空间返还给自由存储区,然后接着 delete 第二个指针,此时则自由存储区可能会被破坏。

These kinds of errors in manipulating dynamically allocated memory are considerably easier to make than they are to track down and fix.

操纵动态分配的内存时,很容易发生上述错误,但这些错误却难以跟踪和修正。


Deleting a const Object

删除 const 对象

Although the value of a const object cannot be modified, the object itself can be destroyed. As with any other dynamic object, a const dynamic object is freed by deleting a pointer that points to it:

尽管程序员不能改变 const 对象的值,但可撤销对象本身。如同其他动态对象一样, const 动态对象也是使用删除指针来释放的:

     delete pci; // ok: deletes a const object

Even though the operand of the delete expression is a pointer to const int, the delete expression is valid and causes the memory to which pci refers to be deallocated.

即使 delete 表达式的操作数是指向 intconst 对象的指针,该语句同样有效地回收 pci 所指向的内容。

Exercises Section 5.11

Exercise 5.30:

Which of the following, if any, are illegal or in error?

下列语句哪些(如果有的话)是非法的或错误的?

     (a) vector<string> svec(10);
     (b) vector<string> *pvec1 = new vector<string>(10);
     (c) vector<string> **pvec2 = new vector<string>[10];
     (d) vector<string> *pv1 = &svec;
     (e) vector<string> *pv2 = pvec1;

     (f) delete svec;
     (g) delete pvec1;
     (h) delete [] pvec2;
     (i) delete pv1;
     (j) delete pv2;


Team LiB
Previous Section Next Section