什么是 C++ 中的初始化列表?它的作用是什么? 初始化列表和在构造函数体内赋值有什么区别?
1) 什么是 C++ 中的初始化列表?它的作用是什么?
在C++中,初始化列表是一种用于初始化类的数据成员的语法机制,它主要在类的构造函数中使用,以冒号开头,后跟一系列成员变量及其初始化值,多个初始化项之间用逗号分隔。
语法:构造函数():属性1(值1),属性2(值2)... {}
初始化参数列表特点:
- 只能在构造函数中使用
- 初始化的顺序和成员变量的顺序一致,和初始化参数列表的顺序无关
- 常量和引用需要在初始化参数列表中初始化
#include<iostream>
using namespace std;
class A
{
int a;
int b;
int c;
const int d;
int &e;
public:
A( ) :a(1), c(2), b(c),d(2),e(a)
{
cout << a << " " << b << " " << c << endl;
}
A(int a, int b, int c, int d, int e) :a(a), b(b), c(c), d(d), e(e) {}
};
int main()
{
A a;
}
初始化const成员变量:const成员变量在初始化后其值就不能再被改变,因此必须在构造函数的初始化列表中进行初始化。
class MyClass {
public:
MyClass(int x) : constMember(x) {}
private:
const int constMember;
};
初始化引用成员变量:引用在定义时必须被初始化,且之后不能再绑定到其他对象,所以引用成员变量也需要通过初始化列表来初始化。
class MyClass {
public:
MyClass(int& ref) : referenceMember(ref) {}
private:
int& referenceMember;
};
提高效率:对于一些复杂的对象,如包含动态分配内存的成员变量的类,使用初始化列表可以避免不必要的临时对象的创建和析构,从而提高程序的性能。
class MyClass {
public:
MyClass(int size) : ptr(new int[size]) {}
~MyClass() { delete[] ptr; }
private:
int* ptr;
};
调用基类的构造函数:在派生类的构造函数中,初始化列表可以用于调用基类的构造函数,以初始化从基类继承来的成员变量。
class Base {
public:
Base(int x) : baseMember(x) {}
private:
int baseMember;
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x), derivedMember(y) {}
private:
int derivedMember;
};
初始化列表是C++中一种重要的初始化机制,它提供了一种更灵活、高效且必要的方式来初始化类的成员变量,有助于确保对象的正确初始化和提高程序的性能。
2)初始化列表和在构造函数体内赋值有什么区别?
在C++中,初始化列表和在构造函数体内赋值都可以用于为类的数据成员赋初值,但二者在多个方面存在区别,以下是详细介绍:
构造函数是给成员变量赋值的
初始化参数列表是给成员变量初始化的
初始化时机
- 初始化列表:初始化列表是在对象创建时,在进入构造函数体之前进行初始化的,是对数据成员的初始化操作。
- 构造函数体内赋值:是在执行构造函数体时才进行赋值操作,此时数据成员已经被默认初始化或无意义的初始化,然后再将新的值赋给它们。
对不同类型成员的处理
const成员变量:const成员变量必须在初始化列表中进行初始化,因为它一旦初始化就不能再被修改,在构造函数体内赋值会导致编译错误。
class MyClass {
public:
MyClass(int x) : constMember(x) {} // 正确,使用初始化列表初始化const成员
private:
const int constMember;
};
引用成员变量:引用成员变量也必须在初始化列表中初始化,因为引用在定义时就必须绑定到一个对象,之后不能再改变引用的对象,在构造函数体内赋值同样会导致编译错误。
class MyClass {
public:
MyClass(int& ref) : referenceMember(ref) {} // 正确,使用初始化列表初始化引用成员
private:
int& referenceMember;
};
对象成员变量:对于对象成员变量,如果该对象的类没有默认构造函数,那么必须在初始化列表中对其进行初始化,否则可以使用初始化列表或在构造函数体内赋值。
class OtherClass {
public:
OtherClass(int x) {}
};
class MyClass {
public:
MyClass(int x) : obj(x) {} // 正确,使用初始化列表初始化对象成员
private:
OtherClass obj;
};
class AnotherClass {
public:
AnotherClass() {}
void setValue(int x) { value = x; }
private:
int value;
};
class MyAnotherClass {
public:
MyAnotherClass(int x) {
obj.setValue(x); // 正确,对象有默认构造函数,可以在构造函数体内赋值
}
private:
AnotherClass obj;
};
性能差异
初始化列表:对于一些复杂的对象,如包含动态分配内存的成员变量的类,使用初始化列表可以避免不必要的临时对象的创建和析构,从而提高程序的性能。
class MyClass {
public:
MyClass(int size) : ptr(new int[size]) {}
~MyClass() { delete[] ptr; }
private:
int* ptr;
};
构造函数体内赋值:先进行默认初始化或无意义的初始化,再进行赋值操作,可能会涉及到额外的临时对象的创建和销毁,尤其对于复杂对象,可能会导致性能开销。
初始化顺序
初始化列表:成员变量的初始化顺序与它们在类中声明的顺序相同,而与初始化列表中的顺序无关。
class MyClass {
public:
MyClass(int x, int y) : member2(y), member1(member2) {}
private:
int member1;
int member2;
};
在上述代码中,member1先于member2声明,所以先初始化member1,此时member2还未初始化,使用member2初始化member1会导致未定义行为。
构造函数体内赋值:赋值操作按照构造函数体内语句的执行顺序进行,与成员变量的声明顺序无关,但同样要注意使用未初始化的变量可能导致的问题。