简体   繁体   中英

Simple structs: advantages/disadvantages of using non-static member initialization, aggregate initialization or constructors

As of C++11, there seem to be three viable ways to define simple structs. (I know that in C++14 option A and B can be combined, but my question relates to C++11).

// non-static member initialization
struct PointA
{
    double x = 0.0;
    double y = 0.0;
};
PointA pA1;
PointA pA2; pA2.x = 2.0; pA2.y = -1.0;  // can this be made more elegantly ?

// aggregate initialization
struct PointB
{
    double x;
    double y;
};
PointB pB1 = { 0.0,  0.0 };  // or does it suffice to write PointB pB1; here?
PointB pB2 = { 2.0, -1.0 };

// constructor
struct PointC
{
    double x;
    double y;
    PointC(double initX = 0.0, double initY = 0.0) : x(initX), y(initY) { }
};
PointC pC1;
PointC pC2( 2.0, -1.0 );

What are the advantages/disadvantages of using each of them ? And which should be preferred in which circumstances in C++11 ? (and would this change if C++14 combined member and aggregate initialization was available ?)

A constructor's job is to establish a user defined class invariant .

If there is no user defined class invariant then a constructor's job is just to make sure that all data members have valid values, ie are not indeterminate (such values can lead to Undefined Behavior when used).

When the only class invariant is that “data members have valid values”, the secondary job, then a constructor is only needed as a way to make the class idiot-proof, so that a novice cannot use it to declare an uninitialized variable. But this has the cost of making the class non-POD. These concerns must be balanced, but it's worth keeping in mind that you can always define a POD class plus a derived class that has constructor.


Regarding the first code example,

struct Point
{
    double x = 0.0;
    double y = 0.0;
};

Point a1;
Point a2; a2.x = 2.0; a2.y = -1.0;  // can this be made more elegantly ?

Due to the data member initializers this class effectively has a constructor like:

Point::Point()
    : x( 0.0 ), y( 0.0 )
{}

And this prevents compilation with Visual C++ 2015 of a declaration like

Point point = { 5.0, 6.0 };

The declaration is accepted by MinGW g++ 5.1.0. I don't know the formal of this. But as a matter of practical programming, the different compiler behaviors means that .


Regarding the second code example,

// aggregate initialization
struct Point
{
    double x;
    double y;
};
Point b1 = { 0.0,  0.0 };  // or does it suffice to write PointB pB1; here?
Point b2 = { 2.0, -1.0 };

Whether it suffices to write just Point b1; depends:

  • If this is a namespace scope declaration then b1 is zero-initialized first of all, so there it's OK.

  • If this is a local declaration then there is no automatic zero-initialization, and so omitting the explicit initialization would give you indeterminate values, leading to UB.

However, you can reduce the declaration to just

Point b1 = {};

or

Point b1{};

depending on your general conventions.

In c++14, I prefer

struct PointA
{
    double x = 0.0;
    double y = 0.0;
};

which allows

PointA pA1; // {0., 0.}
PointA pA2 = { 2.0, -1.0 };

but also

PointA pA3 = { 2.0 }; // {2., 0.}

which may be undesirable.

In c++11 (or if you want to forbid some constructors):

struct PointD
{
    PointD() : PointD(0, 0) {}
    PointD(double x, double y) : x(x), y(y) {}

    double x;
    double y;
};

You can rely on default constructor.

You can simplify any constructor(s) you define.

No it doesn't suffice to write PointB pB1. But you can just put PointB pB1{{},{}};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM