编程学习biancheng.45soft.com 本站提供了各种在线教程和资料,供大家学习与参考.
编程学习
当前位置: 主页 > C++ > 复制控制 > C++ 复制控制 析构函数

C++ 复制控制 析构函数

构造函数的一个用途是自动获取资源。例如,构造函数可以分配一个缓冲区或打开一个文件,在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源。析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充。



何时调用析构函数

撤销类对象时会自动调用析构函数:

// 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

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


动态分配的对象只有在指向该对象的指针被删除时才撤销。如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄漏,而且,对象内部使用的任何资源也不会释放。


当对象的引用或指针超出作用域时,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(而不是对象的引用)超出作用域时,才会运行析构函数。


撤销一个容器(不管是标准库容器还是内置数组)时,也会运行容器中的类类型元素的析构函数:

{
   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

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



何时编写显式析构函数

许多类不需要显式析构函数,尤其是具有构造函数的类不一定需要定义自己的析构函数。仅在有些工作需要析构函数完成时,才需要析构函数。析构函数通常用于释放在构造函数或在对象生命期内获取的资源。


如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。


析构函数并不仅限于用来释放资源。一般而言,析构函数可以执行任意操作,该操作是类设计者希望在该类对象的使用完毕之后执行的。



合成析构函数

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


撤销内置类型成员或复合类型的成员没什么影响。尤其是,合成析构函数并不删除指针成员所指向的对象。



如何编写析构函数

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


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

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

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



13.3 练习题

什么是析构函数?合成析构函数有什么用?什么时候会合成析构函数?什么时候一个类必须定义自己的析构函数?

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

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

理解复制控制成员和构造函数的一个良好方式是定义一个简单类,该类具有这些成员,每个成员打印自己的名字:

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

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

下面的代码段中发生了多少次析构函数的调用?

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;
   // ...
}



编程学习 C++ 复制控制 析构函数 转载请保留此行.谢谢.

C++