构造函数在对象创建时自动调用,用于初始化成员,使对象自建立时起处于有效状态。函数名与类名相同、无返回类型。C++ 支持默认构造、带参构造、委托构造、初始化列表与 explicit。本节在类与对象基础上说明常见形式及与编译器合成规则的关系。

带参构造与初始化列表

成员初始化在构造函数体执行之前完成。对 const 成员、引用成员、以及没有默认构造函数的成员,必须在冒号后的成员初始化列表中初始化:

C++ 构造函数与初始化列表运行
#include <iostream>
#include <string>

class User {
public:
    User(const std::string& name, int id)
        : name_(name), id_(id) {}

    void print() const {
        std::cout << id_ << " " << name_ << "\n";
    }

private:
    std::string name_;
    int id_;
};

int main() {
    User u("Ada", 1);
    u.print();
    return 0;
}

输出:

1 Ada

若在函数体内对成员赋值,则先默认构造再赋值,对部分类型效率更低且对某些成员不可行。

默认构造与重载

#include <iostream>

class Point {
public:
    Point() : x_(0), y_(0) {}
    Point(double x, double y) : x_(x), y_(y) {}

    void show() const {
        std::cout << x_ << ", " << y_ << "\n";
    }

private:
    double x_, y_;
};

int main() {
    Point p1;
    Point p2(3, 4);
    p1.show();
    p2.show();
    return 0;
}

典型输出:0, 0 与 3, 4。

编译器生成的构造

  • 若未声明任何构造函数,编译器可生成合成的默认、拷贝、移动构造(在满足条件时)。
  • 一旦定义了带参构造,不再自动生成默认构造;需要无参构造时应显式 Point() = default; 或自行编写。
  • 含引用或不可默认构造的成员时,合成默认构造可能被删除。

explicit 与单参数构造

class Meter {
public:
    explicit Meter(int value) : value_(value) {}

    int value() const { return value_; }

private:
    int value_;
};

void f(Meter m) {}

int main() {
    // f(100);           // 错误:禁止 int 隐式转为 Meter
    f(Meter(100));       // 显式构造
    return 0;
}

explicit 阻止隐式转换,减少 f(100) 一类意外调用。多参数构造也可标记 explicit(C++11 起)。

委托构造

一个构造函数可在初始化列表中调用同类另一构造函数,避免重复代码:

class Widget {
public:
    Widget() : Widget(0) {}
    Widget(int n) : n_(n) {}

private:
    int n_;
};

成员初始化顺序

成员按类内声明顺序初始化,与初始化列表书写顺序无关。若成员之间存在依赖,声明顺序须合理,否则可能使用未初始化成员。

与析构的配对

构造建立资源,析构函数释放资源。手写资源管理时须保证异常安全;现代 C++ 中多由智能指针与容器承担,见规则三/五的演进讨论。

本节说明了构造函数与初始化列表。下一节介绍封装与访问控制:public、private 与接口设计。