初始化 class 的私有成员变量
Initializing private member variables of a class
我提前道歉,因为我的一些措辞可能不是 100% 正确。
我会有这样的class:
class ClassName {
private:
AnotherClass class2;
public:
ClassName();
~ClassName();
...
在这个 class 的构造函数中,除其他外,我把行
ClassName::ClassName() {
AnotherClass class2;
}
这就是我认为您应该在 C++ 中初始化对象的方式,但是我注意到(通过 GDB)正在创建两个 AnotherClass 对象。一次在构造函数定义上,然后再次在我的初始化行上。这背后的原因是什么?如果我想使用像 AnotherClass(int a, int b)
这样更复杂的构造函数怎么办,它会创建一个临时对象然后在不久之后创建正确的对象吗?
您在构造函数中所做的是创建另一个变量,该变量仅在构造函数内部。
实际上,如果您什么都不做,AnotherClass
中的默认构造函数将为 class2
对象调用。
如果要显式,可以使用构造函数初始化列表:
ClassName::ClassName()
: class2()
{
}
最后一个方法也是您在 AnotherClass
中使用参数调用特定构造函数的方式,如果您需要的话。
AnotherClass class2;
在构造函数主体内创建另一个本地对象,该对象在主体的末尾被销毁。这不是 class 成员的初始化方式。
Class 成员在 成员初始化列表 中构造函数签名和主体之间的构造函数主体之前初始化,以 :
开头,如下所示:
ClassName::ClassName() :
class2(argumentsToPassToClass2Constructor),
anotherMember(42) // just for example
{
/* constructor body, usually empty */
}
如果您不想将任何参数传递给 class2
构造函数,则不必将其放入初始化列表中。然后它的默认构造函数将被调用。
如果您只想调用所有 class 成员的默认构造函数,您可以(并且应该)完全省略构造函数。隐式生成的默认构造函数将执行您想要的操作。
您只是在这一行中创建了一个局部变量。私有成员的初始化一般有以下三种方式:
- 默认初始化
如果您在构造函数上什么都不做,编译器将通过调用其默认构造函数(不带参数的 ctr)自动初始化私有成员
- 将它们分配给点击正文中的值
在这种情况下,您必须使用赋值运算符将所需的值分配给私有成员。
ClassName::ClassName()
{
class2 = AnotherClass(a, b, c); // if the class ctr has some parameters
}
- 通过使用初始化列表
在您的情况下,它将类似于:
ClassName::ClassName()
: class2(initial_value)
{
}
这通常是初始化 class 私有成员的最佳和高效选项,因为您可以避免为传递的参数调用复制构造函数。这通常不是问题,除非复制 ctr 包含耗时的操作。同样适用于选项 #2 在这种情况下,您可能会遇到与赋值运算符相同的问题
ClassName::ClassName() {
AnotherClass class2; // this will create local variable only
}
如果 AnotherClass
有默认构造函数,那么编译器会为 class2
对象调用它。
如果你想调用参数化的构造函数,那么你必须按以下方式进行:
ClassName::ClassName() :
class2(arguments)
为什么要使用以及如何使用初始化列表:
考虑以下示例:
// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};
此处编译器按照以下步骤创建 MyClass 类型的对象
- 首先为“a”调用类型的构造函数。
在MyClass()构造函数体内部调用“Type”的赋值运算符进行赋值
变量=一个;
最后,“Type
”的析构函数被“a
”调用,因为它超出了范围。
现在考虑使用 MyClass
() 构造函数和 Initializer List
的相同代码
// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};
使用初始化列表,编译器遵循以下步骤:
- 调用“
Type
”class 的复制构造函数进行初始化:variable(a)
。初始化列表中的参数用于直接复制构造“variable
”。
- “
Type
”的析构函数为“a
”调用,因为它超出范围。
从这个例子中我们可以看出,如果我们在构造函数体内使用赋值,则有三个函数调用:构造函数+析构函数+一个加法赋值运算符调用。如果我们使用 Initializer List,则只有两个函数调用:复制构造函数 + 析构函数调用。
这种分配惩罚在“真实”应用程序中会更多,因为那里会有很多这样的变量。
还有一些场景,您将不得不仅使用初始化列表:
- 基 class 的参数化构造函数只能使用初始化列表调用。
- 用于引用成员的初始化
- 用于非静态常量数据成员的初始化
你所做的是创建一个与你的成员同名的新变量,
通过这样做,你掩盖了你的成员变量。
此外,在此过程中,您的成员构造函数在 ClassName 空初始化列表中被静默调用。
您可以通过两种方式启动 class:
ClassName::ClassName(): class2() {}
或:
ClassName::ClassName() {
this->class2 = AnotherClass();
}
第一种方法更好,有时是必须的。
如果你只为你的成员使用空构造函数,你不会看到区别,除了性能,因为编译器默认在它的初始化列表中初始化成员(“:”之后的部分,如果你不这样做,他默默地为你做......)
但是如果你的成员没有一个空的构造函数,例如:
AnotherClass:: AnotherClass(int a, int b)
如果您尝试使用第二种方式进行初始化,您将收到如下消息:
error: constructor for 'Initiator' must explicitly initialize the member 'class2' which does not have a default constructor
我提前道歉,因为我的一些措辞可能不是 100% 正确。
我会有这样的class:
class ClassName {
private:
AnotherClass class2;
public:
ClassName();
~ClassName();
...
在这个 class 的构造函数中,除其他外,我把行
ClassName::ClassName() {
AnotherClass class2;
}
这就是我认为您应该在 C++ 中初始化对象的方式,但是我注意到(通过 GDB)正在创建两个 AnotherClass 对象。一次在构造函数定义上,然后再次在我的初始化行上。这背后的原因是什么?如果我想使用像 AnotherClass(int a, int b)
这样更复杂的构造函数怎么办,它会创建一个临时对象然后在不久之后创建正确的对象吗?
您在构造函数中所做的是创建另一个变量,该变量仅在构造函数内部。
实际上,如果您什么都不做,AnotherClass
中的默认构造函数将为 class2
对象调用。
如果要显式,可以使用构造函数初始化列表:
ClassName::ClassName()
: class2()
{
}
最后一个方法也是您在 AnotherClass
中使用参数调用特定构造函数的方式,如果您需要的话。
AnotherClass class2;
在构造函数主体内创建另一个本地对象,该对象在主体的末尾被销毁。这不是 class 成员的初始化方式。
Class 成员在 成员初始化列表 中构造函数签名和主体之间的构造函数主体之前初始化,以 :
开头,如下所示:
ClassName::ClassName() :
class2(argumentsToPassToClass2Constructor),
anotherMember(42) // just for example
{
/* constructor body, usually empty */
}
如果您不想将任何参数传递给 class2
构造函数,则不必将其放入初始化列表中。然后它的默认构造函数将被调用。
如果您只想调用所有 class 成员的默认构造函数,您可以(并且应该)完全省略构造函数。隐式生成的默认构造函数将执行您想要的操作。
您只是在这一行中创建了一个局部变量。私有成员的初始化一般有以下三种方式:
- 默认初始化
如果您在构造函数上什么都不做,编译器将通过调用其默认构造函数(不带参数的 ctr)自动初始化私有成员
- 将它们分配给点击正文中的值
在这种情况下,您必须使用赋值运算符将所需的值分配给私有成员。
ClassName::ClassName()
{
class2 = AnotherClass(a, b, c); // if the class ctr has some parameters
}
- 通过使用初始化列表
在您的情况下,它将类似于:
ClassName::ClassName()
: class2(initial_value)
{
}
这通常是初始化 class 私有成员的最佳和高效选项,因为您可以避免为传递的参数调用复制构造函数。这通常不是问题,除非复制 ctr 包含耗时的操作。同样适用于选项 #2 在这种情况下,您可能会遇到与赋值运算符相同的问题
ClassName::ClassName() {
AnotherClass class2; // this will create local variable only
}
如果 AnotherClass
有默认构造函数,那么编译器会为 class2
对象调用它。
如果你想调用参数化的构造函数,那么你必须按以下方式进行:
ClassName::ClassName() :
class2(arguments)
为什么要使用以及如何使用初始化列表:
考虑以下示例:
// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};
此处编译器按照以下步骤创建 MyClass 类型的对象
- 首先为“a”调用类型的构造函数。
在MyClass()构造函数体内部调用“Type”的赋值运算符进行赋值
变量=一个;
最后,“
Type
”的析构函数被“a
”调用,因为它超出了范围。
现在考虑使用 MyClass
() 构造函数和 Initializer List
// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};
使用初始化列表,编译器遵循以下步骤:
- 调用“
Type
”class 的复制构造函数进行初始化:variable(a)
。初始化列表中的参数用于直接复制构造“variable
”。 - “
Type
”的析构函数为“a
”调用,因为它超出范围。
从这个例子中我们可以看出,如果我们在构造函数体内使用赋值,则有三个函数调用:构造函数+析构函数+一个加法赋值运算符调用。如果我们使用 Initializer List,则只有两个函数调用:复制构造函数 + 析构函数调用。
这种分配惩罚在“真实”应用程序中会更多,因为那里会有很多这样的变量。
还有一些场景,您将不得不仅使用初始化列表:
- 基 class 的参数化构造函数只能使用初始化列表调用。
- 用于引用成员的初始化
- 用于非静态常量数据成员的初始化
你所做的是创建一个与你的成员同名的新变量,
通过这样做,你掩盖了你的成员变量。
此外,在此过程中,您的成员构造函数在 ClassName 空初始化列表中被静默调用。
您可以通过两种方式启动 class:
ClassName::ClassName(): class2() {}
或:
ClassName::ClassName() {
this->class2 = AnotherClass();
}
第一种方法更好,有时是必须的。 如果你只为你的成员使用空构造函数,你不会看到区别,除了性能,因为编译器默认在它的初始化列表中初始化成员(“:”之后的部分,如果你不这样做,他默默地为你做......) 但是如果你的成员没有一个空的构造函数,例如:
AnotherClass:: AnotherClass(int a, int b)
如果您尝试使用第二种方式进行初始化,您将收到如下消息:
error: constructor for 'Initiator' must explicitly initialize the member 'class2' which does not have a default constructor