目录
一、构造函数的概念
构造函数是一种特殊的成员函数,其核心功能是初始化对象而非创建对象。需要明确的是:
-
对象内存分配:在C++中,对象的内存空间(如栈上的局部对象)在进入作用域时就已经分配完成,构造函数的作用是在这块已分配的内存上进行初始化工作。
-
替代Init函数 :构造函数的设计初衷是取代传统C风格中手动调用的Init函数,通过自动调用的特性确保对象总是处于有效状态。
例如,以下日期类中的Date
成员函数就是一个典型的构造函数:
cpp
class Date {
public:
// 构造函数(带默认参数)
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year;
int _month;
int _day;
};
当创建Date
类对象时,编译器会自动调用该构造函数对新对象进行初始化。
二、构造函数的特性
构造函数是一种特殊的成员函数,它具有以下核心特征:
-
命名与类名相同:构造函数必须与所属类同名。
-
无返回值 :没有返回值类型(不是void,而是根本不写返回类型)
-
自动调用 :**对象实例化时(创建类对象时)**编译器自动调用对应的构造函数。
-
在对象的整个生命周期内只调用一次
-
主要职责是初始化对象成员,而非创建对象(对象的内存空间(实例化的时候)在调用构造函数前已分配)
-
支持重载 :一个类可以有多个构造函数,通过参数列表区分:
cppclass Date { public: Date() { /* 无参构造 */ } Date(int year) { /* 单参构造 */ } Date(int y, int m, int d) { /* 三参构造 */ } };
三、默认构造函数的三种形式
C++中的默认构造函数指不需要参数即可调用的构造函数,具体包括:
-
编译器自动生成的无参构造函数:当类中没有显式定义任何构造函数时,编译器自动生成的无参构造函数
-
用户显式定义的无参构造函数:显式定义的无参数构造函数
-
用户定义的全缺省参数构造函数:所有参数都有默认值的构造函数
重要限制:
这三种形式不能同时存在(即 一个类只能有一个默认构造函数 ),否则会导致调用歧义。例如无参构造函数和全缺省构造函数虽然语法上构成重载,但实际调用时会产生冲突。
四、编译器生成的构造函数:
-
如果类中没有显式定义构造函数,编译器会自动生成一个无参默认构造函数
-
一旦用户显式定义了任何构造函数,编译器不再自动生成默认构造函数
-
自动生成的构造函数行为:
-
对内置类型成员(int、float、指针等)不做初始化处理(保持随机值)(值不确定,取决于内存状态)
-
对自定义类型成员,会调用编译器自动生成的该类型的默认构造函数进行初始化
-
cpp
#include <iostream>
using namespace std;
class Stack {
public:
Stack(int n = 4) { /*...*/ } // Stack的构造函数
// ...
};
class MyQueue {
public:
// 编译器生成的默认构造函数会调用Stack的构造函数
// 初始化pushst和popst
private:
Stack pushst; // 自定义类型成员
Stack popst; // 自定义类型成员
};
编译器生成构造函数的局限性
考虑以下示例:
cpp
#include <iostream>
using namespace std;
class Date {
public:
void Print() {
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year; // 内置类型
int _month; // 内置类型
int _day; // 内置类型
};
int main() {
Date d1; // 调用编译器生成的默认构造函数
d1.Print(); // 输出随机值
return 0;
}

输出结果将是随机值,因为编译器生成的默认构造函数不会初始化内置类型成员,达不到我们想要的效果。这说明了为什么多数情况下我们需要自己编写构造函数,以确保成员变量被正确初始化。
五、构造函数使用示例与注意事项
日期类示例
cpp
class Date {
public:
// 1. 无参构造函数
Date() {
_year = 1;
_month = 1;
_day = 1;
}
// 2. 带参构造函数
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
// 3. 全缺省构造函数(与无参构造冲突)
/*Date(int year = 1, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}*/
void Print() {
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
使用注意事项(重要!!!)
默认构造函数的调用:
cpp
Date d1; // 正确:调用默认构造函数
Date d2(); // 错误:编译器会认为这是函数声明
构造函数冲突:
-
如果同时提供无参构造和全缺省构造,会导致调用歧义
-
编译器报错:
error C2512: "Date": 没有合适的默认构造函数可用
内置类型初始化问题:
-
编译器生成的构造函数不保证初始化内置类型
-
实际开发中应显式定义构造函数确保初始化
六、最佳实践建议
-
优先考虑使用全缺省参数的构造函数,提高灵活性
-
对于包含内置类型成员的类,建议显式定义构造函数:避免未定义行为
-
如果确实不需要特殊初始化,考虑使用
= default
:当需要编译器生成默认构造函数时,显式声明更清晰cppDate() = default; // 显式要求编译器生成默认构造函数
-
注意初始化顺序 :成员变量的初始化顺序取决于它们在类中的声明顺序 ,而非构造函数初始化列表中的顺序
-
使用初始化列表(而非构造函数体内赋值)进行成员初始化,效率更高:
cppDate(int year = 0, int month = 1, int day = 1) : _year(year), _month(month), _day(day) {}
理解构造函数的这些特性对于编写正确、高效的C++代码至关重要,特别是在涉及对象初始化和资源管理时,是掌握C++面向对象编程的重要基础。在实际开发中,合理的构造函数设计可以显著提高代码的健壮性和可维护性。