【发布时间】:2020-05-11 03:17:19
【问题描述】:
在 C++ 中,初始化列表和在构造函数中赋值而不是每个方法的外观有什么区别? 我的意思是使用一个而不是另一个的优点是什么,为什么在幻灯片(下)的给定示例中仅适用于初始化? (我没找到,希望能加点资源)
【问题讨论】:
标签: c++ class initialization
在 C++ 中,初始化列表和在构造函数中赋值而不是每个方法的外观有什么区别? 我的意思是使用一个而不是另一个的优点是什么,为什么在幻灯片(下)的给定示例中仅适用于初始化? (我没找到,希望能加点资源)
【问题讨论】:
标签: c++ class initialization
在构造函数中使用初始化列表是一步过程,即它在声明对象的那一刻初始化对象。它调用复制构造函数。
而使用分配是两步过程,即定义对象然后分配它。定义对象调用默认构造函数,然后赋值调用赋值运算符。因此,昂贵的操作。
在C++中,类的常量或引用数据成员变量只能在初始化列表中初始化,不能在构造函数体中使用赋值。
常量和引用数据成员变量都具有必须在声明时初始化的属性。因此,只有在构造函数中使用初始化列表,因为初始化列表在声明时初始化类成员变量,而如果构造函数主体在声明后初始化数据成员,则赋值。
在某些情况下,构造函数内的数据成员初始化不起作用,必须使用初始化列表。以下是此类情况。
#include<iostream> using namespace std; class Test { const int t; public: Test(int t):t(t) {} //Initializer list must be used int getT() { return t; } }; int main() { Test t1(10); cout<<t1.getT(); return 0; }
#include<iostream> using namespace std; class Test { int &t; public: Test(int &t):t(t) {} //Initializer list must be used int getT() { return t; } }; int main() { int x = 20; Test t1(x); cout<<t1.getT()<<endl; x = 30; cout<<t1.getT()<<endl; return 0; }
#include <iostream> using namespace std; class A { int i; public: A(int ); }; A::A(int arg) { i = arg; cout << "A's Constructor called: Value of i: " << i << endl; } // Class B contains object of A class B { A a; public: B(int ); }; B::B(int x):a(x) { //Initializer list must be used cout << "B's Constructor called"; } int main() { B obj(10); return 0; }
【讨论】:
如果不使用初始化列表,则在到达构造函数主体之前,将默认构造类的数据成员:
class Foo{
private:
int bar;
public:
Foo(int _bar){//bar is default constructed here
bar = _bar; //bar is assigned a new value here
}
};
这对于基本类型(如int)来说不是什么大问题,因为默认构造函数并不昂贵。但是,如果数据成员没有默认构造函数,或者默认构造后赋值比直接构造更昂贵,则可能会成为问题:
//Bar does not have a default constructor, only a copy constructor
class Bar{
public:
Bar() = delete; //no default constructor
Bar(const Bar& bar); //copy constructor only
};
class Foo{
private:
Bar bar;
public:
Foo(const Bar& _bar){
//this will not compile, bar does not have a default constructor
// or a copy assignment operator
bar = _bar;
}
Foo(const Bar& _bar) : bar(_bar){//this will compile, copy constructor for bar called
}
};
一般来说,使用初始化列表可以获得更高效的代码。
【讨论】:
Bar()=delete 显式删除了 Bars 默认构造函数。这可以防止编译器构造一个。因此,Bar 没有任何构造函数。