const correctness - How much initialization can/should be in C++ Initialization lists -
this first post. believe aware of best practices on stackoverflow not 100%. believe there no specific post addresses interrogation; hope it's not vague.
i trying figure out practices writing c++ constructors medium-to-heavy-duty work.
pushing (all?) init work initialization lists seems idea 2 reasons cross mind, namely:
resource acquisition initialization
as far know, simplest way of guaranteeing members initialized correctly @ resource acquisition make sure what's inside parentheses of initialization list correct when evaluated.
class { public: a(const b & b, const c & c) : _c(c) { /* _c allocated , defined @ same time */ /* _b allocated content undefined */ _b = b; } private: b _b; c _c; }
const
class membersusing initialization lists correct way of using
const
members can hold actual content.class { public: a(int m, int n, int p) : _m(m) /* correct, _m initialized m */ { _n = n; /* incorrect, _n initialized undefined value */ *(const_cast<int*>(&_p)) = p; /* technically valid, ugly , not particularly raii-friendly */ } private: const int _m, _n, _p; }
however problems seem affect on usage of initialization lists:
order
member variables initialized in order declared in class definition, write them in order in constructor initialization list. writing them in different order makes code confusing because won't run in order see, , can make hard see order-dependent bugs.
http://isocpp.github.io/cppcoreguidelines/cppcoreguidelines#s-discussion
this important if initialize value using value initialized in list. example:
a(int in) : _n(in), _m(_n) {}
if
_m
defined before_n
, value @ initialization undefined.i ready apply rule in code, when working other people causes code redundancy , forces reading 2 files @ once. not acceptable , error-prone.
solution — initialize using data ctor arguments.
solution's problem — keeping work in init list without inner dependency means repeating operations. example:
int in_to_n(int in) { /* ... */ return n; } int modify(int n) { /* ... */ return modified; } a::a(int in) : _n(in_to_n(in)) , _n_modified(modify(in_to_n(in))) {}
for tiny bits of repeated operations believe compilers can reuse existing data don't think 1 should rely on significant work (and don't think it's done if calling noninlined separate code).
how work can put in list?
in previous example, called functions compute attributes initialized to. these can plain/lambda functions or static/nonstatic methods, of current class or of another. (i don't suggest using nonstatic methods of current class, might undefined usage according standard, not sure.)
i guess not in big problem, 1 needs make special efforts in clarity keep intent of code clear if writing big classes big work way.
also, when trying apply solution previous problem, there independent work can when initializing instance... gets big if have long sequence of attributes initialize inner dependencies. it's starting program, translated initialization list; guess not c++ supposed transitioning into?
multiple inits
one computes 2 variables @ once. setting 2 variables @ once in init list means either:
using ugly intermediate attribute
struct inneradata { b b; c c; }; /* must exported class definition (ugly) */ class { public: a(const d & input) : _inner(work(input)) , _b(_inner.b) , _c(_inner.c) {} private: b _b; c _c; inneradata _inner; }
this awful , forces useless copies.
or ugly hack
class { public: a(const d & input) : _b(work(input)) {} private: b _b; c _c; b work(const d & input) { /* ... work ... */ _c = ...; } }
this more awful , doesn't work
const
or non-builtin type attributes.
keeping stuff
const
sometimes can take of ctor figure out value give attribute, making sure
const
, , therefore moving work initialization list, can seem constrained. won't give full example, think computing data default filename, computing full filename data, checking if corresponding file exists set const boolean, etc.i guess it's not fundamental problem, seems intuitively more legible in body of ctor, , moving init list correct initialization of const field seems overkill. maybe i'm imagining things.
so here's hard part: asking specific question! have faced similar problems, did find better solution, if not what's lesson learn — or there i'm missing here?
i guess problem i'm pretty trying move work init list when search compromise of state initiated , leave work later. feel init list play bigger role in making modern c++ code haven't seen them pushed further basic usage yet.
additionally, i'm not convinced why values initialized in order, , not in order of list. i've been orally told it's because attributes in order on stack , compiler must guarantee stack data never above sp. i'm not sure how that's final answer... pretty sure 1 implement safe arbitrarily reordered initialization lists, correct me if i'm wrong.
in code:
class { public: a(const b & b, const c & c) : _c(c) { /* _c allocated , defined @ same time */ /* _b allocated content undefined */ _b = b; } private: b _b; c _c; }
the constructor calls b::b()
, b::operator=
may problem if of these doesn't exist, expensive or not implemented correctly raii , rule-of-three guidelines. rule of thumb prefer initializer list if possible.
Comments
Post a Comment