成员顺序依赖如何影响C ++中的初始化列表?

时间:2015-11-24 18:00:48

标签: c++ coding-style

当我阅读帖子https://isocpp.org/wiki/faq/ctors#empty-parens-in-object-decl时,它有一个示例代码。

class MyString {
public:
  MyString(const MyString& s);              // copy constructor
  // ...
protected:
  unsigned len_;  // ORDER DEPENDENCY
  char*    data_;  // ORDER DEPENDENCY
};
MyString::MyString(const MyString& s)
  : len_ (s.len_)
  , data_(new char[s.len_ + 1u])       
{                  ↑↑↑↑↑↑   // not len_
  memcpy(data_, s.data_, len_ + 1u);
}                        ↑↑↑↑   // no issue using len_ in ctor's {body}

在子部分Is it moral for one member object to be initialized using another member object in the initializer expression?What if one member object has to be initialized using another member object?,它说:

“在构造函数的初始化列表中,避免在此对象的后续初始值设定项的初始化表达式中使用此对象中的一个成员对象是最简单和最安全的。由于此准则,后面的构造函数使用s.len_ + 1u而不是len_ + 1u,即使它们是等价的.s。前缀避免了不必要的和可避免的顺序依赖。“

< - 为什么s.len_len_相当? s.len_引用s的{​​{1}},但len_引用len_;在我几天前提出的问题why copy constructor use private property directly in C++中,LogicStuff表示访问s.len_是有效的,因为它适应this实例。似乎这两个答案是无关的。

< - 为什么说const前缀避免了不必要的和可避免的顺序依赖?在这种情况下,任何人都可以详细解释顺序依赖吗?

2 个答案:

答案 0 :(得分:1)

  

为什么s.len_和len_等效?

它们在中是等价的。 len_只是在初始化列表中复制s.len_的值,因此它们都会存储相同的大小。

  

为什么会这样说。前缀避免了不必要的和可避免的   订单依赖?任何人都可以在此解释顺序依赖   情况详情?

这与构造函数中class/struct的成员及其初始化列表之间的顺序依赖关系有关。例如,这是不正确的:

struct Foo
{
    int x, y;
    Foo(): y(0), x(0) {} // wrong initialization order
};

这是正确的:

struct Foo
{
    int x, y;
    Foo(): x(0), y(0) {} // right initialization order
};

此初始化顺序可能是错误的来源。类的成员变量的任何类型的更改/重新排序都需要更新构造函数的初始化列表以匹配新的顺序。我们无法避免这种情况。

但是,在列出的示例中(版本#1):

MyString::MyString(const MyString& s)
  : len_ (s.len_)
  , data_(new char[s.len_ + 1u])    

...如果我们这样做(版本#2):

MyString::MyString(const MyString& s)
  : len_ (s.len_)
  , data_(new char[len_ + 1u])    

...我们将在初始化列表中有两个不同的位置,具体取决于len_初始化的顺序,而不仅仅是一个。{1}}。紧接在上面的代码(#2)会很好,但是如果你之后编辑了类数据成员,那么它更容易出现人为错误。

正因为如此,前一个版本(#1)减少了依赖于初始化顺序的位置数量,因此更容易避免错误(现在和将来)。

答案 1 :(得分:1)

  

在这种情况下,任何人都可以详细解释订单依赖吗?

len_在您的类定义中data_之前声明。因此,无论初始化列表中项目的顺序如何,len_始终始终首先进行初始化。

您的成员变量在您的班级(自上而下)中出现的顺序是它们将被初始化的顺序。初始化列表不会改变排序。

那么如果len_出现在班级data_之后会发生什么,会发生什么问题?我们来看看:

如果你的班级看起来像这样:

class MyString {
public:
  MyString(const MyString& s);              // copy constructor
  // ...
protected:
  char*    data_;    
  unsigned len_;  
};

然后你这样做了:

MyString::MyString(const MyString& s)
  : len_ (s.len_)  
  , data_(new char[len_ + 1u])  // using uninitialized len_ here.     
{ 
   // now len_ is initialized and can be used.                 
   memcpy(data_, s.data_, len_ + 1u);
}         

引入了一个错误,因为len_在调用new char[]时尚未初始化,即使初始化列表恰好首先提到len_。这就是为什么使用s.len_保证在初始化列表中工作的原因,无论成员变量的声明顺序如何。