Should I use virtual 'Initialize()' functions to initialize an object of my class?

后端 未结 13 1519
孤独总比滥情好
孤独总比滥情好 2020-12-17 14:33

I\'m currently having a discussion with my teacher about class design and we came to the point of Initialize() functions, which he heavily promotes. Example:

13条回答
  •  鱼传尺愫
    2020-12-17 14:51

    Some members simply must have values at construction (e.g. references, const values, objects designed for RAII without default constructors)... they can't be constructed in the initialise() function, and some can't be reassigned then.

    So, in general it's not a choice of constructor vs. initialise(), it's a question of whether you'll end up having code split between the two.

    Of bases and members that could be initialised later, for the derived class to do it implies they're not private; if you go so far as to make bases/members non-private for the sake of delaying initialisaton you break encapsulation - one of the core principles of OOP. Breaking encapsulation prevents base class developer(s) from reasoning about the invariants the class should protect; they can't develop their code without risking breaking derived classes - which they might not have visibility into.

    Other times it's possible but sometimes inefficient if you must default construct a base or member with a value you'll never use, then assign it a different value soon after. The optimiser may help - particularly if both functions are inlined and called in quick succession - but may not.

    • [constructors] can't be overridden by derived classes

    ...so you can actually rely on them doing what the base class needs...

    • [constructors] can't call virtual functions

    The CRTP allows derived classes to inject functionality - that's typically a better option than a separate initialise() routine, being faster.

    Arguments for Initialize() functions:

    • derived class can completely replace initialization code

    I'd say that's an argument against, as above.

    • derived class can do the base class initialization at any time during its own initialization

    That's flexible but risky - if the base class isn't initialised the derived class could easily end up (due to oversight during the evolution of the code) calling something that relies on that base being initialised and consequently fails at run time.

    More generally, there's the question of reliable invocation, usage and error handling. With initialise, client code has to remember to call it with failures evident at runtime not compile time. Issues may be reported using return types instead of exceptions or state, which can sometimes be better.

    If initialise() needs to be called to set say a pointer to nullptr or a value safe for the destructor to delete, but some other data member or code throws first, all hell breaks loose.

    initialise() also forces the entire class to be non-const in the client code, even if the client just wants to create an initial state and ensure it won't be further modified - basically you've thrown const-correctness out the window.

    Code doing things like p_x = new X(values, for, initialisation);, f(X(values, for initialisation), v.push_back(X(values, for initialisation)) won't be possible - forcing verbose and clumsy alternatives.

    If a destroy() function is also used, many of the above problems are exacerbated.

提交回复
热议问题