目录
1. c++中如果不写构造函数,自定义类型的成员变量如何进行初始化
根据我搜索到的信息,C++ 中如果不写构造函数,自定义类型的成员变量的初始化方式取决于它们的类型和位置。
1、如果是内置类型(如 int,double,char 等),那么它们会被默认初始化,即静态对象初始化为 0,堆或栈对象初始化为随机数(参考1)。
2、如果是复合类型( 如 string,vector 等),那么它们会调用默认构造函数进行初始化(参考2)
3、如果是引用类型或 const 类型,那么它们必须显式初始化,否则编译器不会为类生成默认构造函数(参考3)。
2 初始化列表是什么
初始化列表是一种用于初始化对象的语法,它以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化值
示例代码如下:
class foo {
public:
int i;
int j;
foo(int x, int y) : i(x), j(y) {} // 初始化列表
};
使用初始化列表的原因有以下几点:
1、对于内置类型,使用初始化列表和在构造函数体内赋值没有太大区别,但对于类类型,使用初始化列表可以避免额外的默认构造和赋值操作,提高效率。
2、对于常量成员、引用成员或没有默认构造函数的类类型成员,必须使用初始化列表进行初始化,否则会编译错误。
3、初始化列表可以保证成员按照声明的顺序进行初始化,而不是按照出现在列表中的顺序,这样可以避免一些潜在的错误。
3 在初始化列表中只初始化部分成员,剩下的成员还会进行初始化吗
是的,剩下的成员还会进行初始化,只是不是用初始化列表中指定的值,而是用默认的方式初始化(参考1)。
如果剩下的成员是内置类型,那么它们会被默认初始化,即静态对象初始化为0,堆或栈对象初始化为随机数(参考2)。
如果剩下的成员是类类型,那么它们会调用默认构造函数进行初始化(参考3)。
如果剩下的成员是引用类型或 const 类型,那么它们必须显式初始化,否则编译器不会为类生成默认构造函数。例如:
class foo {
public:
int i;
int j;
string s;
const int k;
foo(int x) : i(x) {} // 只用初始化列表初始化i
};
在这个例子中,j 会被默认初始化为 0 或随机数,s 会调用 string 的默认构造函数进行初始化,但 k 必须在初始化列表中显式初始化,否则会编译错误。
4 什么是默认构造函数
1、 默认构造函数是一种没有参数或者所有参数都有默认值的构造函数。
2、默认构造函数是在没有显式提供初始化值时调用的构造函数。
3、如果一个类没有定义任何构造函数,编译器会为它合成一个默认构造函数,但这个合成操作只有在构造函数真正需要被调用的时候才会发生。
4、合成的默认构造函数可能什么也不做,也可能是对某些成员进行初始化。例如:
示例代码如下:
class A {
public:
int i;
string s;
A() {} // 显式定义的默认构造函数
};
class B {
public:
int i;
string s;
B(int x) : i(x) {} // 非默认构造函数
};
class C {
public:
int i;
string s;
// 没有定义任何构造函数,编译器会合成一个默认构造函数
};
int main() {
A a; // 调用显式定义的默认构造函数
B b; // 错误,没有默认构造函数
C c; // 调用合成的默认构造函数
}
举一个所有参数都有默认值的构造函数:
class Point {
public:
double x;
double y;
Point(double x_ = 0.0, double y_ = 0.0) : x(x_), y(y_) {} // 所有参数都有默认值的构造函数
};
5 编译器自动生成的默认构造函数会对类成员进行初始化吗
编译器自动生成的默认构造函数是否会对类成员进行初始化,取决于类成员的类型和类的特征。一般来说,编译器自动生成的默认构造函数会对以下类成员进行初始化(参考1):
1、类类型的成员,会调用其默认构造函数进行初始化。
2、基类,会调用其默认构造函数进行初始化。
3、虚基类,会调用其默认构造函数进行初始化。
4、含有虚函数的类,会对虚表指针进行初始化。
5、引用类型或 const 类型的成员,必须在初始化列表中显式初始化,否则编译器不会为类生成默认构造函数。
编译器自动生成的默认构造函数不会对以下类成员进行初始化:
1、内置类型或复合类型的成员,会被默认初始化,即静态对象初始化为0,
2、堆或栈对象初始化为随机数。
3、拥有默认成员初始化器的非静态数据成员,会按照初始化器的值进行初始化。
例如:
class A {
public:
int i; // 内置类型,不会被初始化
string s; // 类类型,会调用string的默认构造函数进行初始化
};
class B : public A {
public:
double d; // 内置类型,不会被初始化
B() = default; // 显式要求编译器生成默认构造函数
};
class C {
public:
int& r; // 引用类型,必须显式初始化
const int c; // const类型,必须显式初始化
C(int x) : r(x), c(x) {} // 显式提供一个构造函数
// C() = default; // 错误,编译器不会生成默认构造函数
};
class D {
public:
virtual void f() {} // 虚函数,会对虚表指针进行初始化
int i = 42; // 拥有默认成员初始化器,会按照42进行初始化
};
6 编译器自动生成的默认构造函数如何对类成员初始化
编译器自动生成的默认构造函数会对类成员进行初始化,是通过初始化列表的方式进行的(参考1)。初始化列表中会按照类成员的声明顺序,而不是出现在列表中的顺序,调用相应的构造函数或赋值操作(参考2)(参考3)。例如:
代码如下:
class A {
public:
int i;
string s;
A() = default; // 编译器自动生成的默认构造函数
};
class B : public A {
public:
double d;
B() = default; // 编译器自动生成的默认构造函数
};
int main() {
B b; // 调用编译器自动生成的默认构造函数
}
编译器自动生成的默认构造函数相当于:
A::A() : i(), s() {} // 初始化列表中调用int和string的默认构造函数
B::B() : A(), d() {} // 初始化列表中调用A和double的默认构造函数
7 初始化列表是编译器自动生成的吗
初始化列表可以是编译器自动生成的,也可以是用户自定义的。当用户没有为类提供任何构造函数,或者使用了关键字 default 或 delete 来要求编译器生成默认构造函数时,编译器会自动生成一个初始化列表,用来对类成员进行初始化。
当用户自定义了构造函数时,用户可以在构造函数后面加上一个冒号和一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化值,这样就形成了一个用户自定义的初始化列表,用来对类成员进行初始化。例如:
class Point {
public:
double x;
double y;
Point() = default; // 编译器自动生成的默认构造函数和初始化列表
Point(double x_, double y_) : x(x_), y(y_) {} // 用户自定义的构造函数和初始化列表
};
如果是用户自定义初始化列表,只初始化一部分成员变量,那么剩下的成员变量编译器会怎么处理?
如果是用户自定义初始化列表,只初始化一部分成员变量,那么剩下的成员变量编译器会按照默认的方式进行处理。具体来说,有以下几种情况(参考1):
如果剩下的成员变量是内置类型或复合类型,那么它们会被默认初始化,即静态对象初始化为 0,堆或栈对象初始化为随机数。
如果剩下的成员变量是类类型,那么它们会调用默认构造函数进行初始化。
如果剩下的成员变量是引用类型或 const 类型,那么它们必须在初始化列表中显式初始化,否则编译器不会为类生成默认构造函数。
class A {
public:
int i;
string s;
A(int x) : i(x) {} // 只用初始化列表初始化i
};
class B : public A {
public:
double d;
B(int x) : A(x) {} // 只用初始化列表调用A的构造函数
};
int main() {
A a(10); // s会调用string的默认构造函数进行初始化
B b(20); // d会被默认初始化为0或随机数
}