0

In my code, there are two classes:

  1. Vect3 which represents a vector in 3D i.e. x, y & z
  2. Plane which represents the coefficients in the equation of plane i.e. a, b, c, & d

Now, Vect3 is my base class and Plane is the derived one.

  • Vect3 class (base class):
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 y, 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 (derived class):
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";
}

However, for the following main,

int main() {
    std::vector<float> vec1 = {1.0, 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;
}

I'm getting somewhat weird output:

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

In the plane constructor, using initializer list, I'm already updating _a, _b, & _c first and then passing them to Vect3 constructor. So, ideally, output of printVect3Magnitude() & printAngleWrtAxisDeg() method of object plane and object vect3d should be same.

However, based on the above output, it looks like garbage values are being passed to Vect3 constructor!!


On the other hand, if I modify plane constructor as follow (i.e. rather than passing _a, _b, _c to Vect3 constructor, directly pass input vector plane_coefficients)

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

then I'm getting the expected output:

from Plane class: 1 1 1
1.73205
Angle in degree, wrt X-axis = 54.7356 | wrt Y-axis = 54.7356 | wrt Z-axis = 54.7356
= = = = = = = = = = = =
1.73205
Angle in degree, wrt X-axis = 54.7356 | wrt Y-axis = 54.7356 | wrt Z-axis = 54.7356

But why? Am I mistaken regarding the order in which the class members are being updated and passed to the base class constructor (before it gets called) in the initializer list?

Milan
  • 1,743
  • 2
  • 13
  • 36
  • 1
    Yes, the [base class will be initialized before the members of the derived class](https://stackoverflow.com/questions/15366621/are-parent-class-constructors-called-before-initializing-variables), so you can't initialize the base from derived members. – alain Aug 21 '20 at 00:43
  • 1
    Crank up the warning level for you compiler. It might then tell you that the members will not be initialized in the order you've listed them. – 1201ProgramAlarm Aug 21 '20 at 02:56
  • @alain, thanks for sharing that question link. So, basically, no matter in which order I initialize the member variables, the base class constructor would always get called first! – Milan Aug 21 '20 at 15:47
  • @1201ProgramAlarm, right now, I'm not using `cmake`, just running my program using `g++ -std=c++11 main.cpp` command. So, is it possible to set warning level in that command? – Milan Aug 21 '20 at 15:49

1 Answers1

1

Did you notice the compiler warnings? Regardless of how you order the members and base classes in the constructor initializer list, they will be constructed in the declaration order and destructed in the reverse declaration order. Since base list is declared before the block that nests all members, all base sub-objects precede members while construction. If - in the constructor initializer list - the initializer for members and/or bases is miss-placed, a diagnostic warning is generated, but the code compiles . And if a member or base is initialized to the value of another base/member declared after itself, UB will result - as the OP has already observed.

Best, FM.

Red.Wave
  • 2,790
  • 11
  • 17
  • thanks for answering, Right now, I'm not using `cmake`, just running my program using `g++ -std=c++11 main.cpp` command. So, I didn't get any compiler warnings! Is it possible to get that in the command I'm using? – Milan Aug 21 '20 at 15:52
  • At least use the `-Wall` switch but you can check this too: https://stackoverflow.com/questions/5088460/flags-to-enable-thorough-and-verbose-g-warnings – Red.Wave Aug 21 '20 at 16:28