2016-12-30 96 views
0

这部分是一个风格问题,部分是一个正确性问题。提交下列样品(一类,其与包含嵌入的报头数据块涉及的带向下):C++ - 混合默认成员初始值设定项和成员初始化列表 - 坏主意?

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_; 
}; 

首先,技术正确性...是正确的,作为写,scratch_size_会先初始化,然后header_,然后data_length_? (constexpr项目是编译时文字常量,并没有考虑到初始化顺序。)如何声明初始化程序,是默认成员初始化(int foo = 5)还是成员初始化程序列表,对于初始化顺序,但是,重要的仅仅是成员声明的顺序?我发现this answer,引用关于初始化顺序的ISO规范,我收集的是scratch_size_出现在成员初始化列表中,而不是其他成员被赋予默认成员初始化程序的顺序;只有在其他成员之前声明scratch_size_才是重要的。推测如果scratch_size_被声明为最后,则header_data_length_会(不期望地/错误地)被首先初始化。

样式问题...混合这两种初始化样式是不好的样式吗?我的方法是,成员初始化列表中的项目(scratch_size_)取决于传递给构造函数的参数,而其余类成员是从其他类成员派生的。显然,如果初始化器依赖于构造器参数,那么它已经进入成员初始化列表。我是否应该将全部初始值设定项抛入成员初始化列表中,并放弃默认成员初始值设定项?国际海事组织,这可能会使代码有点难以遵循。思考?

+0

我相信这是正确的..不认为它很审美个人,但其他人可能会有所不同 –

+4

应删除[] scratch_;' –

+1

“*传统初始化*”没有什么“传统”有关默认成员初始值设定项。 –

回答

3

default member initializers的存在并不影响初始化类型的子对象的顺序。它将总是在声明顺序。

风格取决于你。您拥有的构造函数越多,使用DMI所获得的收益就越多,因为您没有重复初始化并不会改变。同时,如果您开始制作覆盖DMI的构造函数,那么可能会导致对该对象初始状态的混淆。关键是尽量不要惊讶发生了什么事情。

然而,在你的具体情况下,我会说你有太多的变量。你的阵列应该是一个std::vector<uint8_t>。使用header_指针是可疑的,但是可以辩护(虽然它的初始化不正确;您需要使用placement-new来满足C++的对象模型)。但data_length_可以根据需要计算。

您拥有的会员越少,他们如何初始化的机会就越少。

+0

我正在学习C++,它来自20年来编写嵌入式C,以及90年代后期的一些C#和一点C++。非常适合点。我知道它关于DMI和多个构造函数;我还从您给出的一个链接中看到,构造函数的成员初始化方法将覆盖DMI,这是有道理的。放置新增完全有意义;这是一种覆盖语言的正确方法,不会像我所做的那样滥用指针。重新:太多的变量,我可能会犯过早的优化;单线功能并不是一个很大的性能损失。感谢您的帮助。 –

-1

无论const或非const是否使用初始化列表进行初始化,都已在所有论坛,类成员中建议。在你的情况下,它可能不是一个问题,但是当这个类用于扩展它可以给出问题。