繁体   English   中英

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

[英]C++: Execution of programm(constructors, destructors, assignment operators, etc)

我试图理解为什么以下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 <<构造函数; 然后,该值被返回并用'='分配给test1,在这种情况下,该值将是'copy ctor',因此也是cout <<'copy ctor';

cout << test1 << endl;

在这里,我期望cout <<“来自测试的Hello”; 重载的'<<'的cuz

vector<Test> vec;
vec.push_back(Test());

在这里,我期望创建一个实例,并使用no参数ctor将其推入vec(1),所以cout <<“” Constructor“ << endl;

然后我期望test1和vec(1)在程序结束时超出范围,所以2x“ cout <<” destructor“;”

因此,总体而言,我的期望是:

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的值时,该值也会在程序结尾处被销毁,这就是为什么需要额外的析构函数的原因。 或者至少我是这样认为的。 如果我错了,请纠正我。

我也不明白为什么我在第一个'cout <<“ Constructor”;之后没有得到'cout <<“ Copy ctor”'。 不是测试test1 = getTest();' 呼叫复制ctor?

另外,如果可能的话,请帮助我理解该程序的流程,以便我理解为什么它输出其功能并更好地理解c ++中的OOP。 感谢您的阅读。

此行为是由于复制省略 从标准§[class.copy]¶31:

当满足某些条件时,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用,也允许实现忽略类对象的复制/移动构造。 在这种情况下,实现将忽略的复制/移动操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本来应该以较晚的时间发生。没有优化就销毁。 在以下情况下允许复制/移动操作的这种省略,称为复制删除(可以合并以消除多个副本):
[...]
—当尚未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualtype类型的类对象时,可以通过将临时对象直接构造为以下形式来省略复制/移动操作:省略的复制/移动目标[...]

在这种情况下, Test test1 = getTest()满足我上面引用的条件。 将在getTest创建的临时Test对象未绑定到引用。 因此,可以直接在getTest的返回值中getTest 此优化也称为返回值优化或RVO。 同样,由于getTest的返回值(本身是一个临时对象)未绑定到引用,因此可以直接将其构造到test1

对于vec.push_back(Test()); ,您创建的临时Test对象必须复制或移动到vector的内部存储中。 在这种情况下,无法进行复制省略,因为您创建的临时对象在传递给push_back时将绑定到引用,并且标准中列出的其他情况均不适用。 这意味着必须创建两个Test对象:一个(使用您的临时对象)使用其默认构造函数构造,以及另一个(副本的vector )由copy-constructor创建。

所以这是程序的实际输出。 我禁用了编译器优化并优化了返回值。 以下是程序的输出

constructor
copy constructor
Destructor
copy constructor
Destructor
Hello from test
constructor
copy constructor
Destructor
Destructor
Destructor

让我们分解一下。 第一个构造函数,复制和析构函数调用均与getTest()方法相关。 当您通过Test()创建对象实例时,最初会调用constructor这将输出constructor 然后需要返回该值,因为所有这些都已编译成汇编对象,需要放置在堆栈上,尤其是需要放置在函数调用堆栈之前的位置(在之前或之后我可能是错误的)但是,在此之后保留一个位置作为返回值)。 因此,实例被复制到堆栈上的那个位置,并输出copy constructor 这导致函数完成并调用创建的原始实例的析构函数。

接下来是Test test1 = getTest(); 然后,将函数调用中保留位置处的实例复制到变量test1从而生成copy constructor ,然后保留位置处的实例将销毁,并输出Destructor

然后显示Hello from testHello from test ,由于流运算符使用引用,因此不会复制任何内容。 因此,不会调用任何构造函数或析构函数。

vec.push_back(Test());时输出最后一个constructor调用vec.push_back(Test()); 专门称为Test() 创建此实例后,向量类将在push_back调用期间将该实例复制到向量类的数组中的某个位置。 剩下的三个析构函数将调用test1 ,在push_back期间创建的实例以及存储在vector类中的实例。

希望对您有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM