为什么析构函数在对象被删除之前运行
Why destructor runs before object is being deleted
当我 运行 此代码析构函数在对象删除之前启动。
代码在这里:
#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;
}
当我运行程序输出为:
Im alive: John
Im no longer alive: John
Im alive: Jack
Im no longer alive: John
Im no longer alive: Jack
Im alive: Jake
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
Jack
输入后:
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
所以在每个 push_back() 之后的所有析构函数 运行s。输出操作运行良好,因此对象仍然存在。
对于第一个析构函数 运行s 4 次!为什么?
您正在将临时对象作为参数传递给 push_back
。正在复制临时文件,之后其生命周期到期,因此被销毁。
这是相关的代码片段:
vector <Testi> a;
a.push_back(Testi("John"));
Testi("John")
创建一个新的临时 Testi 对象。
push_back
将该对象复制到向量中。
- 然后删除临时对象。
因此意外的构造函数和析构函数调用来自临时对象的创建和删除。您可以使用 emplace_back
避免额外的临时对象和副本,这将直接在向量中构造对象。
为了更清楚地添加 class 的复制构造函数,例如,按照此演示程序中显示的以下方式
#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
如果您重载复制和移动构造函数(并为方便起见跟踪实例),您将看到发生了什么there
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 Im alive: John
1 => 2 move constructor John
1 Im no longer alive: John
3 Im alive: Jack
3 => 4 move constructor Jack
2 => 5 copy constructor John
2 Im no longer alive: John
3 Im no longer alive: Jack
6 Im alive: Jake
6 => 7 move constructor Jake
5 => 8 copy constructor John
4 => 9 copy constructor Jack
5 Im no longer alive: John
4 Im no longer alive: Jack
6 Im no longer alive: Jake
Jack
8 Im no longer alive: John
9 Im no longer alive: Jack
7 Im no longer alive: Jake
1 - 作为参数传递给 push_back()
的临时对象
2 - 存储在通过移动(或复制)创建的向量中的对象
3 - 另一个临时对象
4 - 存储在向量中的对象
5 - 对象 2 的副本,因为 vector 需要重新分配
等等。但这将为您提供实施的总体思路(以及为什么有人可能想要 reserve()
)。标准不限制究竟应该使用什么。这里还要说一下版本:
- 在 C++11 之前包含的类型应该是可复制构造和可复制分配的
- 从 C++11 开始,限制适用于特定函数;
push_back()
包含的类型应为 CopyInsertable and MoveInsertable
这应该让您对实施有大致的期望。
当我 运行 此代码析构函数在对象删除之前启动。
代码在这里:
#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;
}
当我运行程序输出为:
Im alive: John
Im no longer alive: John
Im alive: Jack
Im no longer alive: John
Im no longer alive: Jack
Im alive: Jake
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: JakeJack
输入后:
Im no longer alive: John
Im no longer alive: Jack
Im no longer alive: Jake
所以在每个 push_back() 之后的所有析构函数 运行s。输出操作运行良好,因此对象仍然存在。 对于第一个析构函数 运行s 4 次!为什么?
您正在将临时对象作为参数传递给 push_back
。正在复制临时文件,之后其生命周期到期,因此被销毁。
这是相关的代码片段:
vector <Testi> a;
a.push_back(Testi("John"));
Testi("John")
创建一个新的临时 Testi 对象。push_back
将该对象复制到向量中。- 然后删除临时对象。
因此意外的构造函数和析构函数调用来自临时对象的创建和删除。您可以使用 emplace_back
避免额外的临时对象和副本,这将直接在向量中构造对象。
为了更清楚地添加 class 的复制构造函数,例如,按照此演示程序中显示的以下方式
#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
如果您重载复制和移动构造函数(并为方便起见跟踪实例),您将看到发生了什么there
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 Im alive: John
1 => 2 move constructor John
1 Im no longer alive: John
3 Im alive: Jack
3 => 4 move constructor Jack
2 => 5 copy constructor John
2 Im no longer alive: John
3 Im no longer alive: Jack
6 Im alive: Jake
6 => 7 move constructor Jake
5 => 8 copy constructor John
4 => 9 copy constructor Jack
5 Im no longer alive: John
4 Im no longer alive: Jack
6 Im no longer alive: Jake
Jack8 Im no longer alive: John
9 Im no longer alive: Jack
7 Im no longer alive: Jake
1 - 作为参数传递给 push_back()
的临时对象
2 - 存储在通过移动(或复制)创建的向量中的对象
3 - 另一个临时对象
4 - 存储在向量中的对象
5 - 对象 2 的副本,因为 vector 需要重新分配
等等。但这将为您提供实施的总体思路(以及为什么有人可能想要 reserve()
)。标准不限制究竟应该使用什么。这里还要说一下版本:
- 在 C++11 之前包含的类型应该是可复制构造和可复制分配的
- 从 C++11 开始,限制适用于特定函数;
push_back()
包含的类型应为 CopyInsertable and MoveInsertable
这应该让您对实施有大致的期望。