成员顺序依赖性如何影响 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_
引用 s
的 len_
,但 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_
可以保证在初始化列表中工作,而不管成员变量的声明顺序如何。
当我阅读 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_
引用 s
的 len_
,但 len_
引用 this
;在我几天前问的问题 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_
可以保证在初始化列表中工作,而不管成员变量的声明顺序如何。