当我使用这段代码时,输出根据类
中指针的声明顺序而变化#include<iostream>
using namespace std;
class myClass{
int *y,*z,*x;
public:
myClass(int a,int b,int c): x(seta(a)),y(setb(b)),z(setc(c)){}
int* seta(int a){
cout<<a;
return (new int(a));
}
int* setb(int b){
cout<<b;
return (new int(b));
}
int* setc(int c){
cout<<c;
return (new int(c));
}
};
int main(){
myClass m1(1,2,3);
return 0;
}
但是当我像这样改变构造函数时
myClass(int a,int b,int c){
x=seta(a);
y=setb(b);
z=setc(c);
}
则不管指针声明的顺序如何,输出都是123。为什么?
因为类/结构体的成员总是按照声明的顺序初始化。
在第一个示例中,声明的顺序是y, z, x
,因此初始化列表中的执行顺序是
y(setb(b))
z(setc(c))
x(seta(a))
和您在初始化列表中给出的顺序并不重要(但是,正如Klaus指出的,如果顺序不同,您可以从编译器获得警告)。
得到2 3 1
在第二个示例中,y, z, x
被默认初始化(具有未定义的值),然后修改,按照指令
x=seta(a);
y=setb(b);
z=setc(c);
所以你得到1 2 3
,但只是因为y, z, x
被静默初始化而没有使用seta()
setb()
和setc()
在CPPref上阅读
成员初始化式在列表中的顺序是无关的:初始化的实际顺序如下:
- 如果构造函数是用于派生最多的类,则虚拟基类将按照它们在基类声明的深度优先从左到右遍历中出现的顺序进行初始化(从左到右指基类说明符列表中的出现顺序)
- 然后,直接基按从左到右的顺序初始化,因为它们出现在这个类的基说明符列表
然后,非静态数据成员按照类定义中的声明顺序初始化。- 最后,执行构造函数体
。在构造函数体中,初始化的顺序按照您所描述的顺序执行。但是在初始化列表中,顺序是由它们声明的顺序决定的。
成员初始化为,始终按照成员变量声明的顺序。
在构造函数中调用时,顺序将与调用顺序相同。如果你颠倒了调用的顺序,那么调用的顺序也会颠倒。
在构造函数的初始化列表中,变量将按照成员声明的顺序被赋值。如果你编写的初始化列表不符合初始化顺序,编译器应该会给你一个警告。
您的代码示例使用gcc生成以下警告:main.cpp: In constructor 'myClass::myClass(int, int, int)':
main.cpp:267:16: warning: 'myClass::x' will be initialized after [-Wreorder]
267 | int *y,*z,*x;
| ^
main.cpp:267:10: warning: 'int* myClass::y' [-Wreorder]
267 | int *y,*z,*x;
| ^
main.cpp:269:9: warning: when initialized here [-Wreorder]
269 | myClass(int a,int b,int c): x(seta(a)),y(setb(b)),z(setc(c)){}
| ^~~~~~~
如果你按照第二个代码示例中给出的方式编写构造函数,变量仍然会按照定义的顺序初始化,但是初始化首先使用空指针,在此之后,它会用构造函数主体中给出的新值覆盖已经初始化的值。
编译器会把未使用的默认初始化优化掉,因为它没有副作用,但是你必须明白,在构造函数体中赋值变量不是变量初始化,而是赋值!