我正在从结构C转向OOP C ++,我经常在C ++中声明/定义构造函数时,特别使用“:
”符号作为运算符。我粗略地理解了这种风格的使用,但有人用这个构造函数定义向我解释了精确的编程技术。
例如:1
class time_stamp
{
public:
time_stamp(time &t_time)
: m_time(t_time)
{}
~time_stamp()
{
m_time.update(); // as soon as I'm destroyed, update the time
}
private:
time &m_time;
};
例如:2
class threaded_class
{
public:
threaded_class()
: m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
~threaded_class()
{
pthread_mutex_destroy(&m_mutex);
}
/** Some other member declarations */
}
请在以下2个示例的下面代码行中解释我使用“:
”
time_stamp(time &t_time) : m_time(t_time){}
和
threaded_class(): m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
答案 0 :(得分:7)
冒号字符:
用于表示构造函数成员初始化列表。在这里,您可以初始化类的成员或调用基类构造函数。
C ++标准版n3337 12.6.2 § 3:
mem-initializer-list可以使用any初始化基类 class-or-decltype,表示基类类型。
C ++标准版n3337 12.6.2 § 7:
使用mem-initializer中的expression-list或braced-init-list 初始化指定的子对象(或者,如果是 委托构造函数,完成类对象) 用于直接初始化的8.5的初始化规则。
示例:
class Foo {
int a;
};
如果你希望整数a
在调用构造函数后得到确定的值,你必须在构造函数中给出a
这个值。有两种选择:
Foo::Foo() {
a = 70;
}
在其成员初始化列表
中Foo::Foo() : a( 70) {
}
始终合法,永远不会低于构造函数体内的赋值效率,并且通常效率更高。关于初始化列表非常重要的事情是它允许指导初始化类成员,省略受此类过程影响的成员的默认构造。
正如Scott Myers在他的“Effective C ++”中指出的那样,如果你没有为类成员指定一个初始化参数,那么它的默认构造函数将被调用。当您稍后在类构造函数中对其执行赋值时,您将在成员变量上调用operator=
。这将对成员函数进行两次调用:一次用于默认构造函数,另一次用于赋值。您可以通过指定初始值设定项来省略第一个调用。正如Scott Myers在他的“Effective C ++”中指出的那样:“从纯粹实用的角度来看,有时候必须使用初始化列表。特别是const
和参考成员只能初始化,永远不会分配”。
(至少)同样重要的是,成员不会按照它们在初始化列表中的出现顺序进行初始化,而是按类中的声明顺序进行初始化。请记住这一点,以避免错误,如
/* trying to allocate very large block of memory
as a result of initializing a vector with
uninitialized integer: std::vector<int> v( N)
*/
class SearchEngine {
std::vector<int> v;
int N;
explicit SearchEngine( std::vector<int> const& keys)
: N( keys.size()), v( N), {
C ++标准版n3337 8.5.4 § 1:
列表初始化是从a初始化对象或引用 支撑-初始化列表。这样的初始化程序称为初始化程序列表, 和列表中逗号分隔的初始化子句称为 初始化列表的元素。初始化列表可以为空。 列表初始化可以在直接初始化或复制中进行 - 初始化上下文;列表初始化在 直接初始化上下文称为直接列表初始化和 调用复制初始化上下文中的列表初始化 副本列表初始化。 [注意:可以使用列表初始化 - 作为 变量定义中的初始值设定项(8.5)
- 作为初始化者 一个新的表达式(5.3.4)
- 在一份退货声明(6.6.3)
中- 作为一个 函数参数(5.2.2)
- 作为下标(5.2.1)
- 作为一个论据 构造函数调用(8.5,5.2.3)
- 作为a的初始化程序 非静态数据成员(9.2)
- 在mem-initializer(12.6.2)
中- 关于 作业的右侧(5.17)
[例如:
int a = {1};
std :: complex z {1,2};
new std :: vector {“once”, “曾几何时”}; // 4个字符串元素
f({“Nicholas”,“Annemarie”} ); //传递两个元素的列表
return {“Norah”}; //返回列表 一个元素
int * e {}; //初始化为零/空指针
x = 双{1}; //显式构造一个双
的std ::地图 anim = {{“bear”,4},{“cassowary”,2},{“tiger”,7}};
- 结束示例] - 结束注释]
答案 1 :(得分:0)
初始化列表。当您想要在构造之后立即初始化成员对象时,它非常有用。当成员对象不是默认的构造函数时,你必须使用它。
它不仅仅是初始化成员的另一种方式,有时你必须使用它,而且大多数时候你应该使用它来保持代码的一致性。
以下是您必须使用它的情况示例:
struct A
{
const X x; // X has not default constructor
A() : x(some_value) {}
};
即使成员对象具有默认构造函数,也应该通过initialization-list对其进行初始化,以避免重复构造。
struct A
{
string x;
A() : x("Hello") {}
};
在上述情况下,如果您在构造函数的正文中指定"Hello"
,那么您对string::string()
然后x = "Hello";
进行了不必要的调用,可以将其替换只需拨打一次string::string("Hello")
即可。
答案 2 :(得分:0)
这是成员初始化。这是唯一可以初始化成员的初始成员。
如果在大括号内执行此操作,则已调用成员的默认构造函数并为其分配新值。使用冒号语法,您可以决定成员的初始化方式(根据值fortrivial类型和构造函数方面的非常规类型)。