在 C++ 中,类的对象初始化是创建对象时为其成员变量赋予初始值的过程,核心依赖构造函数(类的特殊成员函数)。根据场景和需求,对象初始化有多种方式,下面详细介绍:
一、构造函数:初始化的核心
构造函数是与类同名的特殊成员函数,用于对象创建时的初始化,其特点:
- 无返回值(无需写
void); - 可重载(定义多个参数不同的构造函数);
- 若未手动定义,编译器会生成默认构造函数(无参,仅为成员变量执行默认初始化)。
二、对象初始化的具体方式
1. 默认初始化(无参构造函数)
当类有默认构造函数(无参构造函数)时,可直接创建对象,无需传递参数。
默认构造函数的来源:
- 手动定义:
类名(); - 编译器自动生成(仅当未定义任何构造函数时)。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <iostream>
usingnamespacestd;
classPerson {
private:
string name;
intage;
public:
// 手动定义默认构造函数
Person() {
name ="Unknown";
age = 0;
cout <<"默认构造函数被调用"<< endl;
}
voidshow() {
cout <<"Name: "<< name <<", Age: "<< age << endl;
}
};
intmain() {
// 默认初始化(调用默认构造函数)
Person p1;// 正确:无参构造
p1.show();// 输出:Name: Unknown, Age: 0
return0;
}
|
注意:若手动定义了带参构造函数,编译器不再生成默认构造函数,此时需显式定义,否则Person p1;会报错。
2. 带参数的构造函数初始化
通过自定义带参数的构造函数,在创建对象时传递参数,直接初始化成员变量。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | classPerson {
private:
string name;
intage;
public:
// 带参数的构造函数
Person(string n,inta) {
name = n;// 为成员变量赋值
age = a;
cout <<"带参构造函数被调用"<< endl;
}
voidshow() {
cout <<"Name: "<< name <<", Age: "<< age << endl;
}
};
intmain() {
// 用带参构造函数初始化
Person p2("Alice", 18);// 直接传递参数
p2.show();// 输出:Name: Alice, Age: 18
return0;
}
|
3. 成员初始化列表(推荐)
构造函数的成员初始化列表用于在进入构造函数体之前,直接初始化成员变量(而非在函数体内赋值)。
适用场景:
- 初始化
const成员变量(必须在定义时初始化); - 初始化引用成员(
&,必须在定义时初始化); - 初始化没有默认构造函数的成员对象(如类的成员是另一个类的对象,且该类无默认构造)。
语法:
1 2 3 | 类名(参数列表) : 成员1(值1), 成员2(值2), ... {
// 构造函数体(可空)
}
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | classPerson {
private:
string name;
constintage;// const成员,必须初始化
int& id;// 引用成员,必须初始化
public:
// 成员初始化列表:直接初始化成员
Person(string n,inta,int& i) : name(n), age(a), id(i) {
// 构造函数体可做其他操作,无需再给成员赋值
cout <<"成员初始化列表被使用"<< endl;
}
voidshow() {
cout <<"Name: "<< name <<", Age: "<< age <<", ID: "<< id << endl;
}
};
intmain() {
intnum = 1001;
Person p3("Bob", 20, num);// 传递参数到初始化列表
p3.show();// 输出:Name: Bob, Age: 20, ID: 1001
return0;
}
|
优势:比在构造函数体内赋值更高效(直接初始化,而非先默认构造再赋值)。
4. 拷贝构造函数初始化
用已存在的对象初始化新对象时,会调用拷贝构造函数。
拷贝构造函数的语法(参数必须是同类对象的const引用):
1 2 3 | 类名(const类名& 已有对象) {
// 用已有对象的成员初始化当前对象的成员
}
|
调用场景:
- 用对象 A 初始化对象 B(
类名 B = A;或类名 B(A);); - 对象作为函数参数按值传递;
- 函数返回对象(按值返回)。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | classPerson {
private:
string name;
intage;
public:
// 带参构造函数
Person(string n,inta) : name(n), age(a) {}
// 拷贝构造函数(自定义)
Person(constPerson& other) {
name = other.name;// 拷贝other的name
age = other.age;// 拷贝other的age
cout <<"拷贝构造函数被调用"<< endl;
}
voidshow() {
cout <<"Name: "<< name <<", Age: "<< age << endl;
}
};
intmain() {
Person p4("Charlie", 22);// 先创建p4
Person p5 = p4;// 用p4初始化p5(调用拷贝构造)
Person p6(p4);// 另一种写法(同样调用拷贝构造)
p5.show();// 输出:Name: Charlie, Age: 22
p6.show();// 输出:Name: Charlie, Age: 22
return0;
}
|
注意:若未手动定义拷贝构造函数,编译器会生成默认拷贝构造函数(浅拷贝)。若类中包含指针成员,浅拷贝可能导致内存问题(需手动实现深拷贝)。
5. 列表初始化(C++11 及以上)
使用大括号{}初始化对象,支持多种场景,且更安全(防止窄化转换,如int不能隐式接收double的小数部分)。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | classPerson {
private:
string name;
intage;
public:
// 带参构造函数(支持列表初始化)
Person(string n,inta) : name(n), age(a) {}
voidshow() { cout << name <<", "<< age << endl; }
};
intmain() {
// 列表初始化(调用带参构造)
Person p7{"David", 25};// 等价于 Person p7("David", 25);
Person p8 = {"Eve", 26};// 也支持等号+大括号
p7.show();// David, 25
p8.show();// Eve, 26
return0;
}
|
优势:
- 统一初始化语法(可用于内置类型、数组、容器等);
- 防止窄化转换(如
Person p{123.45, "abc"}会编译报错,因参数类型不匹配)。
6. 委托构造函数(C++11 及以上)
允许一个构造函数调用同一个类的另一个构造函数,减少代码重复。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | classPerson {
private:
string name;
intage;
public:
// 核心构造函数(带参数)
Person(string n,inta) : name(n), age(a) {
cout <<"核心构造函数被调用"<< endl;
}
// 委托构造:调用带参构造函数(默认name为"Unknown",age为0)
Person() : Person("Unknown", 0) {
cout <<"委托构造函数(无参)被调用"<< endl;
}
// 委托构造:调用带参构造函数(仅指定age,name默认"Guest")
Person(inta) : Person("Guest", a) {
cout <<"委托构造函数(仅age)被调用"<< endl;
}
};
intmain() {
Person p9;// 调用无参委托构造 → 核心构造
Person p10(30);// 调用单参委托构造 → 核心构造
return0;
}
|
输出:
核心构造函数被调用
委托构造函数(无参)被调用
核心构造函数被调用
委托构造函数(仅age)被调用
7. 聚合类的初始化
聚合类是一种特殊的类(或结构体),满足:
- 无用户定义的构造函数;
- 无私有 / 保护的非静态成员;
- 无基类;
- 无虚函数。
聚合类可直接用初始化列表初始化成员(按成员声明顺序)。