构造函数在对象创建时自动调用,用于初始化成员,使对象自建立时起处于有效状态。函数名与类名相同、无返回类型。C++ 支持默认构造、带参构造、委托构造、初始化列表与 explicit。本节在类与对象基础上说明常见形式及与编译器合成规则的关系。
带参构造与初始化列表
成员初始化在构造函数体执行之前完成。对 const 成员、引用成员、以及没有默认构造函数的成员,必须在冒号后的成员初始化列表中初始化:
#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 与接口设计。