C++ / ACM 自学笔记 · v5.0

(三) 构造函数、初始化列表与析构函数

上一节先创建对象再调用设置方法;现在希望创建时就准备好数据,并在离开作用域时完成清理。沿一个对象从创建到销毁观察这些操作。

构造函数在对象创建时初始化它;析构函数在对象生命周期结束时执行清理。

  • 构造函数与类同名,没有返回类型,可以重载。
  • 析构函数写为 ~类名(),没有返回类型和参数。
  • explicit 可以阻止构造函数参与某些隐式转换。
  • 成员初始化列表写在函数体之前,如 : name_(name), score_(score)。

注意: 成员初始化顺序取决于它们在类中的声明顺序,不取决于初始化列表的书写顺序。const 成员和引用成员通常需要在初始化阶段给出值。

初始化与赋值发生在不同阶段。 初始化列表直接决定成员如何创建;在构造函数体里写赋值时,成员已经完成初始化,再执行赋值。例如 string 成员在进入函数体前就已经是一个对象,所以优先在初始化列表中提供初值。引用在初始化列表中绑定到对象,const 成员也在这里获得初值;进入函数体时,它们已经完成创建。

下面按执行顺序观察:创建 first 时输出“构造 A”;进入内层作用域创建 second,输出“构造 B”;遇到内层右花括号先销毁 second,输出“析构 B”;之后才执行“离开内层作用域”;main 结束时销毁 first。同一作用域中的普通局部对象按完成构造的相反顺序销毁,这让资源能随作用域自动释放。

// 构造与析构打印对象名;内层 B 先离开作用域,因此先析构 B。
#include <iostream>
#include <string>
using namespace std;

class Trace {
  private:
    string name_;

  public:
    explicit Trace(string name) : name_(name) {
        cout << "构造 " << name_ << '\n';
    }

    ~Trace() {
        cout << "析构 " << name_ << '\n';
    }
};

int main() {
    Trace first("A");
    {
        Trace second("B");
    }
    cout << "离开内层作用域\n";
}

输出:

构造 A
构造 B
析构 B
离开内层作用域
析构 A
局部对象的构造与析构顺序

普通局部对象在离开作用域时销毁;使用 new 创建的对象需要由相应所有者负责释放。

查阅笔记正文与全部题解 · Ctrl / ⌘ K