我试图理解为什么以下C ++程序会输出它的作用。
#include <iostream>
#include <vector>
#include <initializer_list>
#include <memory.h>
using namespace std;
// Constructors and memory
class Test{
static const int SIZE = 100;
int *_pBuffer;
public:
Test(){
cout << "constructor" << endl;
_pBuffer = new int[SIZE]{}; // allocated memory for int[size] and initialised with 'size' 0's
}
Test(int i){
cout << "parameterized constructor" << endl;
_pBuffer = new int[SIZE]{};
for(int i=0; i<SIZE; i++)
{
_pBuffer[i] = 7*i;
}
}
Test(const Test& other) // in the copy constructor we...
{
cout << "copy constructor" << endl;
_pBuffer = new int[SIZE]{}; // allocate the bytes then copy them from the 'other'
memcpy(_pBuffer, other._pBuffer, SIZE*sizeof(int));
}
Test &operator=(const Test &other){
cout << "assignment" << endl;
_pBuffer = new int[SIZE]{}; // allocate the bytes then copy them from the 'other'
memcpy(_pBuffer, other._pBuffer, SIZE*sizeof(int));
return *this;
}
~Test(){
cout << "Destructor" << endl;
delete [] _pBuffer;
}
};
ostream &operator<<(ostream &out, const Test &test)
{
out << "Hello from test";
return out;
}
Test getTest()
{
return Test();
}
int main() {
Test test1 = getTest(); // object gets created with default constructor =>
cout << test1 << endl;
vector<Test> vec;
vec.push_back(Test());
return 0;
}
这就是我期望它的工作方式以及我期望它打印的内容:
Test test1 = getTest();
在这里,我期待这种情况发生:在getTest中,使用没有参数的ctor创建Test实例:cout&lt;&lt;构造函数;然后返回此值并使用&#39; =&#39;分配给test1。在这种情况下,这将是“复制”。因此也是cout&lt;&lt; &#39;复制ctor&#39;;
cout << test1 << endl;
我希望cout&lt;&lt; &#34;你好,来自test&#34 ;;因为重载的&#39;&lt;&lt;&lt;&#39;
vector<Test> vec;
vec.push_back(Test());
在这里,我期待一个实例被创建并推入vec(1),其中没有参数ctor所以cout&lt;&lt; &#34;构造&#34; &LT;&LT; ENDL;
然后我期待test1和vec(1)在程序结束时超出范围,所以2x&#34; cout&lt;&lt; &#34;析构函数&#34 ;; &#34;
总的来说,我的期望是:
cout << constructor;
cout << copy constructor;
cout << hello from test;
cout << constructor;
cout << destructor;
cout << destructor;
然而程序的执行输出是:
constructor
Hello from test
constructor
copy constructor
Destructor
Destructor
Destructor
这与我的期望不同:)。
其中我猜我能理解最终得到的额外析构函数。我想当函数getTest()返回一个赋值给test1的值时,该值也会在程序结束时被销毁,这就是为什么额外的析构函数。或者至少是我的想法。如果我错了,请纠正我。
另外,我不明白为什么我没有得到一个&#39; cout&lt;&lt; &#34;复制ctor&#34;&#39;在第一个&#39; cout&lt;&lt; &#34;构造&#34 ;;&#39 ;.不测试test1 = getTest();&#39;打电话复制ctor?
如果可能的话,请帮助我理解这个程序的流程,这样我就能理解为什么它输出它的功能并更好地理解c ++中的OOP。谢谢你的阅读。
答案 0 :(得分:0)
此行为是由 copy elision 引起的。从标准§[class.copy]¶31:
当满足某些条件时,允许省略实现 复制/移动类对象的构造,即使构造函数 选择用于复制/移动操作和/或析构函数 对象有副作用。在这种情况下,实施处理 省略的复制/移动操作的源和目标只是两个 引用同一个对象的不同方式,以及对它的破坏 该对象发生在两个对象的后期 没有优化就会被破坏。这个省略 复制/移动操作,称为复制省略,是允许的 以下情况(可以合并以消除多个 份):
[...]
- 当一个尚未绑定到引用的临时类对象时 (12.2)将被复制/移动到具有相同的类对象 cv-unqualified类型,可以省略复制/移动操作 将临时对象直接构造到目标中 省略复制/移动 [...]
在这种情况下,Test test1 = getTest()
符合我上面引用的标准。将在Test
中创建的临时getTest
对象未绑定到引用。因此,它可以直接在getTest
的返回值中构造。此优化也称为返回值优化或RVO。此外,由于getTest
(本身是临时对象)的返回值未绑定到引用,因此可以直接构造为test1
。
对于vec.push_back(Test());
,您创建的临时Test
对象必须复制或移动到vector
的内部存储中。在这种情况下,不可能进行复制省略,因为您创建的临时对象在传递给push_back
时会绑定到引用,并且标准中列出的其他任何情况都不适用。这意味着必须创建两个Test
个对象:一个(您的临时)使用其默认构造函数构建,另一个(vector
复制)由复制构造函数创建。
答案 1 :(得分:0)
所以这是程序的实际输出。我已禁用编译器优化和返回值优化。以下是程序的输出
constructor
copy constructor
Destructor
copy constructor
Destructor
Hello from test
constructor
copy constructor
Destructor
Destructor
Destructor
让我们打破这个。第一个构造函数,复制和析构函数调用都与getTest()
方法相关。当您通过Test()
创建对象的实例时,最初会调用构造函数,这会输出constructor
。然后需要返回这个值,因为这些都是编译成汇编对象需要放在堆栈上,特别是需要放在堆栈上函数调用之前的位置上(我之前可能错了或者然而,在为返回值保留一个位置之后)。因此,实例将复制到输出copy constructor
的堆栈上的该位置。这导致函数完成并调用已创建的原始实例的析构函数。
接下来是Test test1 = getTest();
,然后将函数调用的保留位置的实例复制到变量test1
,结果为copy constructor
,然后销毁保留位置的实例输出Destructor
。
然后显示Hello from test
,因为流操作符使用引用,所以不会复制任何内容。因此,不会调用构造函数或析构函数。
在专门调用constructor
vec.push_back(Test());
时输出最后一次Test()
来电。创建此实例,然后向量类在push_back
调用期间将该实例复制到向量类数组中的位置。其余三个析构函数被调用test1
,在push_back
期间创建的实例和存储在向量类中的实例。
我希望这会有所帮助。