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

How Do Member Order Dependencies Influence Initialization Lists In C++?

当我阅读 post 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?,它说:

"In a constructor’s initialization list, it is easiest and safest to avoid using one member object from this object in the initialization expression of a subsequent initializer for this object. Because of this guideline, the constructor that follows uses s.len_ + 1u rather than len_ + 1u, even though they are otherwise equivalent.The s. prefix avoids an unnecessary and avoidable order dependency."

<-- 为什么 s.len_len_ 等价? s.len_ 引用 slen_,但 len_ 引用 this;在我几天前问的问题 中,LogicStuff 说访问 s.len_ 是有效的,因为它适应了 const 实例。看来这两个答案是无关的。

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

why are s.len_ and len_ equivalent?

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

why does it say that s. prefix avoids an unnecessary and avoidable order dependency? Can any one explain order dependency in this situation in detail?

这与 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
};

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

但是,在列出的示例(版本 #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_ 的初始化顺序,我们将在初始化列表中有两个不同的位置,而不仅仅是一个。上面的代码 (#2) 没问题,但如果您之后编辑 class 数据成员,则更容易出现人为错误。

正因为如此,之前的版本(#1)减少了依赖初始化顺序的地方,因此更容易避免错误(现在和将来)。

Can any one explain order dependency detailedly in this situation?

len_ 在 class 定义中的 data_ 之前声明。因此 len_ 总是 首先初始化,而不管初始化列表中的项目顺序如何。

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

那么如果在你的class中data_之后出现len_会发生什么情况,会出现什么问题呢?让我们看看:

如果您的 class 看起来像这样:

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_ 可以保证在初始化列表中工作,而不管成员变量的声明顺序如何。