C ++:程序的执行(构造函数,析构函数,赋值运算符等)

时间:2017-10-11 08:26:24

标签: c++ oop constructor

我试图理解为什么以下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。谢谢你的阅读。

2 个答案:

答案 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期间创建的实例和存储在向量类中的实例。

我希望这会有所帮助。