Team LiB
Previous Section Next Section

13.3. The Destructor

13.3. 析构函数

One purpose of a constructor is to provide for the automatic acquisition of a resource. For example, a constructor might allocate a buffer or open a file. Having allocated the resource in the constructor, we need a corresponding operation that automatically deallocates or otherwise releases the resource. The destructor is a special member function that can be used to do whatever resource deallocation is needed. It serves as the complement to the constructors of the class.


When a Destructor Is Called


The destructor is called automatically whenever an object of its class is destroyed:


     // p points to default constructed object
     Sales_item *p = new Sales_item;
                               // new scope
         Sales_item item(*p);  // copy constructor copies *p into item
         delete p;             // destructor called on object pointed to by p
     }                         // exit local scope; destructor called on item

Variables such as item are destroyed automatically when they go out of scope. Hence, the destructor on item is run when the close curly is encountered.

变量(如 item)在超出作用域时应该自动撤销。因此,当遇到右花括号时,将运行 item 的析构函数。

An object that is dynamically allocated is destroyed only when a pointer pointing to the object is delete d. If we do not delete a pointer to a dynamically allocated object, then the destructor is never run on that object. The object will persist forever, leading to a memory leak. Moreover, any resources used inside the object will also not be released.


The destructor is not run when a reference or a pointer to an object goes out of scope. The destructor is run only when a pointer to a dynamically allocated object is deleted or when an actual object (not a reference to the object) goes out of scope.


Destructors are also run on the elements of class type in a containerwhether a library container or built-in arraywhen the container is destroyed:


         Sales_item *p = new Sales_item[10]; // dynamically allocated
         vector<Sales_item> vec(p, p + 10);  // local object
         // ...
         delete [] p; // array is freed; destructor run on each element
      }   // vec goes out of scope; destructor run on each element

The elements in the container are always destroyed in reverse order: The element indexed by size() - 1 is destroyed first, followed by the one indexed by size() - 2 and so on until element [0], which is destroyed last.

容器中的元素总是按逆序撤销:首先撤销下标为 size() - 1 的元素,然后是下标为 size() - 2 的元素……直到最后撤销下标为 [0] 的元素。

When to Write an Explicit Destructor


Many classes do not require an explicit destructor. In particular, a class that has a constructor does not necessarily need to define its own destructor. Destructors are needed only if there is work for them to do. Ordinarily they are used to relinquish resources acquired in the constructor or during the lifetime of the object.


A useful rule of thumb is that if a class needs a destructor, it will also need the assignment operator and a copy constructor. This rule is often referred to as the Rule of Three, indicating that if you need a destructor, then you need all three copy-control members.


A destructor is not limited only to relinquishing resources. A destructor, in general, can perform any operation that the class designer wishes to have executed subsequent to the last use of an object of that class.


The Synthesized Destructor


Unlike the copy constructor or assignment operator, the compiler always synthesizes a destructor for us. The synthesized destructor destroys each nonstatic member in the reverse order from that in which the object was created. In consequence, it destroys the members in reverse order from which they are declared in the class. For each member that is of class type, the synthesized destructor invokes that member's destructor to destroy the object.

与复制构造函数或赋值操作符不同,编译器总是会为我们合成一个析构函数。合成析构函数按对象创建时的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。对于类类型的每个成员,合成析构函数调用该成员的析构函数来撤销对象。

Destroying a member of built-in or compound type has no effect. In particular, the synthesized destructor does not delete the object pointed to by a pointer member.


How to Write a Destructor


Our Sales_item class is an example of a class that allocates no resources and so does not need its own destructor. Classes that do allocate resources usually need to define a destructor to free those resources. The destructor is a member function with the name of the class prefixed by a tilde (~). It has no return value and takes no parameters. Because it cannot specify any parameters, it cannot be overloaded. Although we can define multiple class constructors, we can provide only a single destructor to be applied to all objects of our class.

Sales_item 类是类没有分配资源因此不需要自己的析构函数的一个例子。分配了资源的类一般需要定义析构函数以释放那些资源。析构函数是个成员函数,它的名字是在类名字之前加上一个代字号(~),它没有返回值,没有形参。因为不能指定任何形参,所以不能重载析构函数。虽然可以为一个类定义多个构造函数,但只能提供一个析构函数,应用于类的所有对象。

An important difference between the destructor and the copy constructor or assignment operator is that even if we write our own destructor, the synthesized destructor is still run. For example, we might write the following empty destructor for class Sales_item:

析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。例如,可以为 Sales_item: 类编写如下的空析构函数:

     class Sales_item {
        // empty; no work to do other than destroying the members,
        // which happens automatically
         ~Sales_item() { }
        // other members as before

When objects of type Sales_item are destroyed, this destructor, which does nothing, would be run. After it completes, the synthesized destructor would also be run to destroy the members of the class. The synthesized destructor destroys the string member by calling the string destructor, which frees the memory used to hold the isbn. The units_sold and revenue members are of built-in type, so the synthesized destructor does nothing to destroy them.

撤销 Sales_item 类型的对象时,将运行这个什么也不做的析构函数,它执行完毕后,将运行合成析构函数以撤销类的成员。合成析构函数调用 string 析构函数来撤销 string 成员,string 析构函数释放了保存 isbn 的内存。units_soldrevenue 成员是内置类型,所以合成析构函数撤销它们不需要做什么。

Exercises Section 13.3

Exercise 13.11:

What is a destructor? What does the synthesized destructor do? When is a destructor synthesized? When must a class define its own destructor?


Exercise 13.12:

Determine whether the NoName class skteched in the exercises on page 481, is likely to need a destructor. If so, implement it.

确定在第 13.1.2 节习题 13.4 中概略定义的 NoName 类是否需要析构函数,如果需要,实现它。

Exercise 13.13:

Determine whether the Employee class, defined in the exercises on page 484, needs a destructor. If so, implement it.

确定在第 13.2 节习题 13.10 中定义的 Employee 类是否需要析构函数,如果需要,实现它。

Exercise 13.14:

A good way to understand copy-control members and constructors is to define a simple class with these members in which each member prints its name:


     struct Exmpl {
         Exmpl() { std::cout << "Exmpl()" << std::endl; }
         Exmpl(const Exmpl&)
           { std::cout << "Exmpl(const Exmpl&)" << std::endl; }
     // ...

Write a class like Exmpl, giving it the copy-control members and other constructors. Now write a program using objects of type Exmpl in various ways: pass them as non-reference and reference parameters; dynamically allocate them; put them in containers, and so forth. Studying which constructors and copy-control members are executed and when can be helpful in cementing your understanding of these concepts.

编写一个像 Exmpl 这样的类,给出复制控制成员和其他构造函数。然后写一个程序,用不同方式使用 Exmpl 类型的对象:作为非引用形参和引用形参传递,动态分配,放在容器中,等等。研究何时执行哪个构造函数和复制构造函数,可以帮助你融会贯通地理解这些概念。

Exercise 13.15:

How many destructor calls occur in the following code fragment?


     void fcn(const Sales_item *trans, Sales_item accum)
         Sales_item item1(*trans), item2(accum);
         if (!item1.same_isbn(item2)) return;
         if (item1.avg_price() <= 99) return;
         else if (item2.avg_price() <= 99) return;
         // ...

Team LiB
Previous Section Next Section