为什么析构函数在删除对象之前运行

Why destructor runs before object is being deleted

本文关键字:运行 对象 删除 析构函数 为什么      更新时间:2023-10-16

当我运行此代码析构函数时,析构函数在对象删除之前启动。

代码在这里:

#include <string>
#include <vector>
using namespace std;
class Testi {
public:
string name;
Testi(string a) : name(a) {
cout << "Im alive: " << name << endl;
}
~Testi() {
cout << "Im no longer alive: " << name << endl;
}
};
int main() {
vector <Testi> a;
a.push_back(Testi("John"));
a.push_back(Testi("Jack"));
a.push_back(Testi("Jake"));
cout << a[1].name;
cin.get();
return 0;
}

当我运行程序输出时是:

我还活着:约翰·我不再活着:约翰·我活着:杰克·我不再活着:约翰·我不再活着:杰克·我活着:杰克·我不再活着:

约翰


·伊姆不再活着:杰克·伊姆不再活着:杰克·伊姆不再活着:




·千斤顶

输入后:

我不再活着:

约翰
我不再活着:杰克
我不再活着:杰克

因此,在每个 push_back() 之后,所有析构函数都运行。输出操作效果很好,因此对象仍然存在。 对于第一个析构函数运行 4 次!为什么?

这是相关的代码段:

vector <Testi> a;
a.push_back(Testi("John"));
  1. Testi("John")创建一个新的临时 Testi 对象。
  2. push_back将该对象复制到矢量中。
  3. 然后删除临时对象。

因此,意外的构造函数和析构函数调用来自临时的创建和删除。 您可以通过使用emplace_back来避免额外的临时和副本,这将直接在向量中构造对象。

为了更清楚地添加类的复制构造函数,例如,按照本演示程序中显示的以下方式

#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Testi {
public:
string name;
Testi(const string &a) : name(a) {
cout << "Im alive: " << name << endl;
}
Testi( const Testi &t ) : name(t.name + "_copy") {
cout << "Im alive: " << name << endl;
}
~Testi() {
cout << "Im no longer alive: " << name << endl;
}
};

int main()
{
{
vector <Testi> a;
a.push_back(Testi("John"));
a.push_back(Testi("Jack"));
a.push_back(Testi("Jake"));
cout << "---------------------" << endl;
for (const auto &item : a) cout << item.name << ' ';
cout << endl << endl;
}
{
cout << "---------------------" << endl;
vector <Testi> a;
a.reserve(3);
a.emplace_back("John");
a.emplace_back("Jack");
a.emplace_back("Jake");
cout << "---------------------" << endl;
for (const auto &item : a) cout << item.name << ' ';
cout << endl << endl;
}
return 0;
}

它的输出可能看起来像

Im alive: John
Im alive: John_copy
Im no longer alive: John
Im alive: Jack
Im alive: John_copy_copy
Im no longer alive: John_copy
Im alive: Jack_copy
Im no longer alive: Jack
Im alive: Jake
Im alive: John_copy_copy_copy
Im alive: Jack_copy_copy
Im no longer alive: John_copy_copy
Im no longer alive: Jack_copy
Im alive: Jake_copy
Im no longer alive: Jake
---------------------
John_copy_copy_copy Jack_copy_copy Jake_copy
Im no longer alive: John_copy_copy_copy
Im no longer alive: Jack_copy_copy
Im no longer alive: Jake_copy
---------------------
Im alive: John
Im alive: Jack
Im alive: Jake
---------------------
John Jack Jake
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake

所以在这个声明中

a.push_back(Testi("John"));

由于表达式Testi("John")创建了一个临时对象。 然后将此对象复制到向量,并向量存储临时对象的副本。在语句的末尾,将删除临时对象。

Im alive: John
Im alive: John_copy
Im no longer alive: John

执行此语句时

a.push_back(Testi("Jack"));

执行相同的操作,只是向量需要重新分配内存以容纳新元素。

Im alive: Jack
Im alive: John_copy_copy
Im no longer alive: John_copy
Im alive: Jack_copy
Im no longer alive: Jack

第一条消息对应于创建与参数Testi("Jack")对应的临时对象。然后由于内存重新分配,向量的当前元素被复制到新的内存范围

Im alive: John_copy_copy
Im no longer alive: John_copy

然后复制新元素并删除临时对象

Im alive: Jack_copy
Im no longer alive: Jack

等等。

如果在向量中保留足够的内存,则不会重新分配内存。此外,如果您将使用emplace_back而不是push_back则不会创建临时对象。在这种情况下,输出将是

Im alive: John
Im alive: Jack
Im alive: Jake

Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake

您正在将临时对象作为参数传递给push_back. 临时正在复制,之后其生存期到期,因此它被销毁。

如果您要重载复制并移动构造函数(并为方便起见跟踪实例),您将看到那里发生了什么

struct Testi {
static int instanceCount;

std::string name;
int instanceIndex;

Testi(Testi&& other) : name(other.name), instanceIndex(++instanceCount) {
std::cout <<other.instanceIndex << " => " << instanceIndex << " move constructor " << name << std::endl;
}

Testi(const Testi& other) : name(other.name), instanceIndex(++instanceCount) {
std::cout <<other.instanceIndex << " => " << instanceIndex << " copy constructor " << name << std::endl;
}
Testi(std::string a) : name(a), instanceIndex(++instanceCount) {
std::cout << instanceIndex << " Im alive: " << name << std::endl;
}
~Testi() {
std::cout << instanceIndex << " Im no longer alive: " << name << std::endl;
}
};
int Testi::instanceCount = 0;

1 我还活着:约翰
1 => 2 移动构造函数 约翰
1 我不再活着:约翰
3 我还活着:杰克
3 => 4 移动构造函数 杰克
2 => 5 复制构造函数约翰
2 我不再活着: 约翰
3 我不再活着: 杰克
6 我还活着: 杰克
6 => 7 移动构造函数 杰克
5 => 8 复制构造函数 约翰
4 => 9 复制构造函数 Jack


5 我不再活着: 约翰
4 我不再活着: 杰克
6 我不再活着: 杰克

杰克8 我不再活着:约翰
9 我不再活着:杰克
7 我不再活着:杰克

1 - 作为参数传递给push_back()
相关文章: