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 members

    using 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

Popular posts from this blog

sequelize.js - Sequelize group by with association includes id -

android - Robolectric "INTERNET permission is required" -

java - Android raising EPERM (Operation not permitted) when attempting to send UDP packet after network connection -