声明时新的 C++11 成员初始化功能是不是使初始化列表过时?
Posted
技术标签:
【中文标题】声明时新的 C++11 成员初始化功能是不是使初始化列表过时?【英文标题】:Has the new C++11 member initialization feature at declaration made initialization lists obsolete?声明时新的 C++11 成员初始化功能是否使初始化列表过时? 【发布时间】:2014-07-31 17:50:20 【问题描述】:使用 C++11,我们现在可以在头声明中初始化类成员:
class aClass
private:
int mInt100;
public:
aClass();
~aClass();
;
所以我有点困惑。传统上,构造函数中的初始化列表已用于成员初始化:
aClass::aClass()
: mInt(100)
...
声明时新的 C++11 成员初始化功能是否使初始化列表过时?如果不是,那么一个比另一个有什么优势?什么情况会使声明时的初始化有利,或者初始化列表有利?什么时候应该使用一个而不是另一个?
【问题讨论】:
显然不是。考虑构造函数参数。 @RobKennedy-IMO 不完全正确:AFAIK,如果您有一个带参数的构造函数,那么您在初始化列表中对其参数所做的任何事情也可以在具有相同成员的构造函数的主体中完成(尽管通常需要更多代码)。在初始化列表中使用构造函数的参数并不是很有用——它似乎没有多大意义:您将参数传递给构造函数,就像其他函数一样:您将在 正文中使用它们 的功能。初始化列表用于初始化:设置默认值、常量等 这不是真的。 const 成员变量不能在构造函数体中初始化;它必须在初始化列表中进行初始化。此外,“初始化”的概念并没有排除使用运行时值,例如来自参数的值。我的构造函数主体经常是empty,因为我使用参数直接初始化成员。此外,如果您关于为什么初始化列表已过时的论点是我们可以改用构造函数主体,那么它们在 在 C++11 之前也已过时,并且您的问题的前提不成立。跨度> @RobKennedy - const 成员是一个很好的例子,同意。 如果你的论点为什么初始化列表已经过时...... - 不完全是:初始化列表允许你使用带有参数的构造函数来初始化你的值,但在正文中你必须调用一个方法setValue(T)
- 传递参数时,该成员已使用默认构造函数初始化。但您也可以在标头中进行初始化,就像在初始化列表中一样。
投反对票 - 请解释。无法解释的反对票不是很有建设性......这个问题的部分答案是相当明显的,真实的 - 其他部分,不是那么多......
【参考方案1】:
不,它们并没有像这篇文章 Get to Know the New C++11 Initialization Forms 在类成员初始化部分中所说的那样过时(强调我的):
请记住,如果同一个数据成员在构造函数中同时具有类成员初始化器和 mem-init,则后者优先。事实上,您可以通过以类成员初始化程序的形式为成员指定默认值来利用此行为,如果构造函数没有显式的 mem-init for那个成员。否则,构造函数的 mem-init 将生效,覆盖类成员初始化程序。 此技术在具有多个构造函数的类中很有用
因此,尽管在类成员初始化中很方便,但它并没有消除对初始化列表的需求,但是这两个特性协同工作,为您提供了一种指定默认值并在需要时覆盖它们的好方法。这似乎也是Bjarne Stroustrup 的看法,他说:
这节省了一些输入,但真正的好处来自具有多个构造函数的类。通常,所有的构造函数都为一个成员使用一个通用的初始化器:
并提供具有通用初始化器的成员示例:
class A
public:
A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run")
A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run")
A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run")
int a, b;
private:
HashingFunction hash_algorithm; // Cryptographic hash to be applied to all A instances
std::string s; // String indicating state in object lifecycle
;
然后说:
hash_algorithm 和 s 都有一个默认值这一事实在混乱的代码中丢失了,并且在维护期间很容易成为问题。相反,我们可以分解数据成员的初始化:
class A
public:
A(): a(7), b(5)
A(int a_val) : a(a_val), b(5)
A(D d) : a(7), b(g(d))
int a, b;
private:
HashingFunction hash_algorithm"MD5"; // Cryptographic hash to be applied to all A instances
std::string s"Constructor run"; // String indicating state in object lifecycle
;
注意:C++11 中的缺点
在 C++11 中使用类成员初始化有一个缺点,因为它使一个类成为非聚合类,我们不能再使用aggregate initialization,这可能会令人惊讶。在 C++14 中移除了这个限制,情况并非如此。请参阅:C++11 aggregate initialization for classes with non-static member initializers 了解更多详情。
【讨论】:
我想我也会默认a
和b
,我想知道为什么Bjarne没有?
@MooingDuck a
和 b
在不同的构造函数中具有不同的值,因此在每种情况下都没有相同的默认值。这符合他的逻辑。
a
和 b
肯定有不同的值,但它们确实在每种情况下都有相同的默认值。 7->a 和 5->b。只是有时会覆盖默认值。
@MooingDuck 这是一个合理的观点,但由于它是一个定义稀疏的示例,因此我没有强烈的观点。
使用HashingFunction hash_algorithm("MD5");
或HashingFunction hash_algorithm = hash_algorithm("MD5");
会改变什么吗?如果它是一个普通的数据类型,比如使用int a = 7;
来设置默认值怎么办。使用=
符号设置默认值是否等同于int a7;
?【参考方案2】:
不,它们并没有过时。
如果您需要构造函数的参数来初始化您的类成员,初始化列表仍然是唯一的方法。
class A
int a=7; //fine, give a default value
public:
A();
;
class B
int b;
public:
B(int arg) : b(arg)
B(int arg, bool b) : b(arg) ...
;
请注意,如果两者都存在,则构造函数的初始化将生效,覆盖类成员初始化,这对于为类成员指定默认值很有用。
【讨论】:
不确定我是否理解:这有什么问题:B(int arg, bool b) this->b=arg; ...
?不需要初始化列表。 IMO 在初始化列表中使用构造函数的参数并不是很有用——它似乎没有多大意义:你将参数传递给构造函数,就像其他函数一样:你将在功能。初始化列表用于初始化:设置默认值、常量等
@Vector 我不同意这里。为什么要浪费时间初始化数据成员(并且类类型的数据成员将总是被初始化)只是在之后立即分配给它?更不用说不可分配的类型(例如const
,引用),它们必须通过初始化来“设置”。 mem-initialiser-list 用于初始化,构造函数应该这样做。除非需要,否则不应分配。
@Angew - 为什么要浪费时间初始化......我会说“也许”......但是 const 值是一个好点。【参考方案3】:
在我看来,类内初始化是对 mem-initializer-lists 的增强。在 C++03 中,未在 mem-initializer-list 中列出的成员始终默认初始化。这意味着类的默认构造函数,而原始类型没有初始化。
类内初始化只允许您指定自己的默认值。有两种查看方式。
一个:如果您的类的大多数/所有构造函数都想为成员提供相同的初始值,请为该成员使用类内初始化程序。对于其他成员,请使用 mem-initializer-lists。当初始值取决于构造函数参数时,您当然必须使用它们。
另一个:为所有成员提供一个类内初始化器,正是你的类的默认构造函数将如何初始化它们。然后,非默认构造函数中的 mem-initializer-lists 获得“它与默认构造的对象有何不同”的语义。
【讨论】:
以上是关于声明时新的 C++11 成员初始化功能是不是使初始化列表过时?的主要内容,如果未能解决你的问题,请参考以下文章