2
#include <iostream>
#include <vector>
#include <algorithm>

class my {
  public:
    my() {
      counter++;
      std::cout << "class constructor" << counter << " \n";}

    ~my() {
      std::cout << "class destructor" << counter << " \n";
      counter--;
    }
  
    static inline int counter = 0;
};

int main()
{
    my v1;
    std::vector<my> my_vec;

    my * p = new my();
    my_vec.push_back(std::move(*p));
    my_vec.push_back(std::move(v1));
}

简单的例子,但是我不明白我做错了什么,结果我得到了比我预期的2个额外的析构函数(预期2)。有人可以解释一下吗?

结果:

class constructor1
class constructor2
class destructor2
class destructor1
class destructor0
class destructor-1
4

2 回答 2

3

逐步分析程序:

my v1;

创建一个实例,调用构造函数。

my * p = new my();

创建另一个实例,调用构造函数。

my_vec.push_back(std::move(*p));

第二个实例的副本被插入到向量中;隐式定义的移动构造函数被调用(它只是复制;不打印输出)。

my_vec.push_back(std::move(v1));

向量为 2 个实例分配新存储,将先前存储的实例复制到新存储中(调用隐式定义的移动构造函数,它只是进行复制,仍然没有输出),并为旧存储中的实例调用析构函数(所以打印第一个析构函数输出)。

然后,向量超出范围,因此它的两个包含的元素被销毁(因此,2 个析构函数调用)。然后,v1超出范围,打印第四个析构函数调用。实例p被泄露,即永远不会被破坏(内存泄露)。

于 2020-07-30T09:50:10.103 回答
1

请看内联...

#include <iostream>
#include <vector>

class my {
  public:
    my() {
      counter++;
      std::cout << "class constructor" << counter << " \n";
      std::cout << "Object Address : " << this << std::endl;
    }


    ~my() {
      std::cout << "class destructor" << counter << " \n";
      std::cout << "Object Address : " << this << std::endl;
      counter--;
    }

    static inline int counter = 0;
};

int main()
{
    my v1;     /* 1. Object on stack (constructor called) */
    std::vector<my> my_vec;

    my * p = new my();   /* 2. Object created on heap (constructor called) */
    my_vec.push_back(std::move(*p));
    my_vec.push_back(std::move(v1));
}

输出是:

class constructor 1 Address : 0x7ffee1488760
class constructor 2 Address : 0x7f9f47400350
class destructor 2 Address : 0x7f9f474026e0
class destructor 1 Address : 0x7f9f474026f1
class destructor 0 Address : 0x7f9f474026f0
class destructor -1 Address : 0x7ffee1488760

堆栈上的对象一旦超出范围即称为析构函数,即堆栈上对象的范围仅限于代码块。在这种情况下,当 main 退出时块结束。

但是堆 ( new) 上的对象是一个潜在的泄漏,如果它在 while 循环中会消耗内存并最终崩溃。对于每个new你需要显式调用delete的,这意味着你必须调用它的析构函数。多亏了stack它为我们做了,但不是heap,而且 c++ 也没有垃圾收集器。

std::move是一个复制操作,因此不调用构造函数。详情在这里

于 2020-07-30T10:13:52.530 回答