jeudi 29 décembre 2016

C++ - Mixing traditional initializers and member initialization lists - bad idea?

This is partly a style question, partly a correctness question. Submit the following sample (a strip-down of a class which deals with a block of data that contains an embedded header):

class Foo {
 public:
  Foo(size_t size)
      : scratch_(new uint8_t[header_length_ + size]),
        size_(header_length_ + size) {
  }
  ~Foo() {
    delete scratch_;
  }
  Foo(const Foo&) = delete;  // Effective C++
  void operator=(const Foo&) = delete;  // Effective C++
 protected:
  struct Header {
    uint32_t a, b, c, d;
  };
  uint8_t * const scratch_;
  size_t const size_;
  Header * const header_ = reinterpret_cast<Header *>(scratch_);
  static constexpr size_t header_length_ = sizeof(Header);
  static constexpr size_t data_offset_ = header_length_;
  size_t const data_length_ = size_ - data_offset_;
};

First, technical correctness... is it correct that as written, scratch_ and size_ will be initialized first, then header_, then data_length_? (constexpr items are compile-time literal constants and don't figure into initialization order.) Is it also correct that how the initializer is declared, be it traditional int foo = 5 initialization or a member initializer list, has no impact on initialization order, but rather, what matters is solely the order in which members are declared? I found this answer, citing the ISO spec regarding initialization order, and what I gathered is that it's unimportant that scratch_ and size_ appear in the member initialization list versus other members that are given traditional initializers; it only matters that scratch_ and size_ are declared before the other members. Presumably if scratch_ and size_ were declared last, then header_ and data_length_ would (undesirably/incorrectly) be initialized first.

Style question... is it bad style to mix these two initialization styles? My approach is that items in the member initialization list (scratch_, size_) depend on the argument(s) passed into the constructor, while the remaining class members are derived from other class members. Obviously, if an initializer depends on a constructor argument, then it has to go into a member initialization list. Should I throw all initializers into the member initialization list regardless, and abandon traditional initialization? IMO, that may make the code a little harder to follow. Thoughts?

Aucun commentaire:

Enregistrer un commentaire