C ++:在初始化器列表中,按什么顺序更新类成员并将其传递给基类构造函数在被调用之前?

问题描述

在我的代码中,有两个类:

  1. Vect3代表3D中的矢量,即xyz
  2. Plane,代表平面方程即abcd的系数

现在,Vect3是我的基类,Plane是派生类。

  • Vect3类(基类):
class Vect3 {
public:
    Vect3(float x,float y,float z);
    Vect3(std::vector<float> vec);

    void printVect3() const;
    void printVect3Magnitude() const;
    void printAngleWrtAxisDeg();

    float rad2deg(float angle_in_rad);

protected:
    float _x = 0.0;
    float _y = 0.0;
    float _z = 0.0;

    float _magnitude = 0.0;

    float _angle_wrt_X = 0.0;
    float _angle_wrt_Y = 0.0;
    float _angle_wrt_Z = 0.0;

    void _doCalculation();
};

Vect3::Vect3(float x,float z) : _x(x),_y(y),_z(z) {
    _doCalculation();
}

Vect3::Vect3(std::vector<float> vec) : _x(vec[0]),_y(vec[1]),_z(vec[2]) {
    _doCalculation();
}

void Vect3::_doCalculation() {
    _magnitude = sqrt(_x * _x + _y * _y + _z * _z);
    _angle_wrt_X = acos(_x / _magnitude);
    _angle_wrt_Y = acos(_y / _magnitude);
    _angle_wrt_Z = acos(_z / _magnitude);
}

void Vect3::printVect3() const {
    std::cout << "x = " << _x << " | y = " << _y << " | z = " << _z << "\n";
}

void Vect3::printVect3Magnitude() const {
    std::cout << _magnitude << "\n";
}

void Vect3::printAngleWrtAxisDeg() {
    std::cout << "Angle in degree,"
              << "wrt X-axis = " << rad2deg(_angle_wrt_X)
              << " | wrt Y-axis = " << rad2deg(_angle_wrt_Y)
              << " | wrt Z-axis = " << rad2deg(_angle_wrt_Z) << "\n";
}

float Vect3::rad2deg(float angle_in_rad) {
    return (angle_in_rad * 180.0 / M_PI);
}
  • Plane类(派生类):
class Plane : public Vect3 {
public:
    Plane(std::vector<float> plane_coefficients);

private:
    float _a = 0.0;
    float _b = 0.0;
    float _c = 0.0;
    float _d = 0.0;
};

Plane::Plane(std::vector<float> plane_coefficients) : _a(plane_coefficients[0]),_b(plane_coefficients[1]),_c(plane_coefficients[2]),_d(plane_coefficients[3]),Vect3(_a,_b,_c) {
    std::cout << "from Plane class: " << _x << " " << _y << " " << _z << "\n";
}

但是,对于以下main

int main() {
    std::vector<float> vec1 = {1.0,1.0,1.0};

    Plane plane1(vec1);
    plane1.printVect3Magnitude();
    plane1.printAngleWrtAxisDeg();

    std::cout << "= = = = = = = = = = = =\n";

    Vect3 vect3d(vec1);
    vect3d.printVect3Magnitude();
    vect3d.printAngleWrtAxisDeg();

    return 0;
}

我的输出有些奇怪:

from Plane class: 3.07795e-41 2.8026e-45 0
0
Angle in degree,wrt X-axis = nan | wrt Y-axis = nan | wrt Z-axis = -nan
= = = = = = = = = = = =
1.73205
Angle in degree,wrt X-axis = 54.7356 | wrt Y-axis = 54.7356 | wrt Z-axis = 54.7356

plane构造函数中,使用初始化列表,我已经在先更新_a_b_c,然后将它们传递给Vect3构造函数。 因此,理想情况下,对象printVect3Magnitude()和对象printAngleWrtAxisDeg()的{​​{1}}和plane方法输出应该相同。

但是,基于上述输出,看来垃圾值正在传递给vect3d构造函数


另一方面,如果我按照以下方式修改Vect3构造函数(即,不直接将plane_a_b传递给_c构造函数传递输入向量Vect3

plane_coefficients

然后我得到了预期的输出

Plane::Plane(std::vector<float> plane_coefficients) : _a(plane_coefficients[0]),Vect3(plane_coefficients) {
    std::cout << "from Plane class: " << _x << " " << _y << " " << _z << "\n";
}

但是为什么呢?我是否对初始化初始化列表中的类成员被更新并传递给基类构造函数(在被调用之前)的顺序感到误解?

解决方法

您是否注意到编译器警告? 无论如何在构造函数初始值设定项列表中对成员和基类进行排序,它们都将按照声明顺序进行构造,并按照相反的声明顺序进行销毁。由于基本列表是在嵌套所有成员的块之前声明的,因此在构造时,所有基本子对象都在成员之前。如果-在构造函数的初始值设定项列表中-成员和/或基址的初始值设定项未正确放置,则会生成诊断警告,但代码会编译。并且,如果将一个成员或基准初始化为在其自身之后声明的另一个基准/成员的值,则将产生UB-正如OP已经观察到的那样。

最好, FM。