C++ – No exceptions C++ and partially constructed objects

ccoding-standardsexceptions

Looking over Joint Strike Fighter Air Vehicle C++ Coding Standard, rule AV 73 states something on the lines: Default c++ constructors should be avoided if that means leaving object in a partially constructed place. This is obvious a good advice but things get complicated when you also disallow exceptions (as it does)

I wonder if they also try to give something else in return to make it practical. E.g. following example needs 2 faze construction if OUT_OF_MEMORY has to be handled (but the problem is more general of course)

class A {
    A() = default; // no way to report construction of B and C
    int Init(); // allocation failure of B/C can be reported only here
    private:
    B* b;
    C* c
}

One way to handle the problem is to have a A* Create() static member but in this case A has to free store allocated or keep an non-fully initialized state inside A and check it inside every member access, which seems not better at all than using a C style

int Initialize(A& out) // stack allocation
int Initialize(unique_ptr<A>& out) // free store allocation

EDIT I posted one possible approach bellow also. I took under consideration that dynamic memory allocation is necessary and the fact that any errors can happen while fully constructing an object. This is a scenario that is plausible for any driver development work.

Best Answer

Mandatory disclaimers

(1) Because people who have seen the code can't say anything about it, and people who can freely comment on it have never seen the actual code, all we can do here is to speculate, speculate, and to speculate. Therefore, here is not an answer, just a speculation.

(2) This is not the typical way I write C++ because most of the projects I work on allows exceptions, at least on a local basis (i.e. not crossing application boundaries), and the coding standard ensures that there are always appropriate exception catchers in the right place. This answer was written as if it is an interesting thought, not as a sharing of experience.


My opinion is that to avoid the issue of partially constructed object (or, "state"), one must first fundamentally change the way parameter (precondition) validation is performed.

The change is this: instead of validating and assigning parameters one-by-one, one must perform the complete validation of all parameters together, in a side-effect-free manner.

In addition to that change, the role of class constructor is also changed. Instead of handling both precondition validation and state initialization, it will "outsource" both to someone else; it will only retain the part of responsibilities that are "failproof" (not capable of failing).

For example, assigning a primitive value (e.g. integer) to a primitive variable is failproof, provided that the primitive variable has valid storage. Another example is the ownership-transfer of a pointer from one smart variable to another.

Some of the biggest advances of C++11 is that smart pointers (and many other things) are moving toward making at least some of the operations "failproof", by giving the option of isolating those "failable" (having the potential of failing) operations into separate methods.


Ultimately, however I must say, the "no exceptions" rule is sometimes impractical for at least some types of application development. How else would one prevent std::bad_alloc without exceptions? Should the system crash-and-burn?

Mission-critical systems prevent out-of-memory issues by ensuring system-wide determinism in memory usage. Everything is preallocated; objects are merely placement-newed on allocators. There is a maximum number of instances prescribed for each type of objects; attempt to exceed the maximum will either be rejected, or result in the yanking of another less-important, not-actively-in-use object.

This, may be why we keep hearing those memes about "this enemy tracking system is capable of simultaneously tracking 256 different objects." When a 257th object wants to be added to the system, one of the least-important object must go. Since none of us commenting here have seen any of the code, this is just a speculation.

Related Topic