对象的奇怪破坏
Strange Destruction of objects
我很困惑为什么析构函数被调用了这么多次。
#include <iostream>
#include <vector>
class Box
{
public:
int x;
Box(int x);
~Box();
};
Box::~Box()
{
std::cout << x << " Destroyed\n";
}
Box::Box(int x)
{
this->x = x;
std::cout << x << " Created\n";
}
int main()
{
std::vector<Box> boxList;
for (int i = 0; i < 3; i++)
{
Box b(i);
boxList.push_back(b);
}
return 0;
}
输出:
0 Created
0 Destroyed
1 Created
0 Destroyed
1 Destroyed
2 Created
0 Destroyed
1 Destroyed
2 Destroyed
main 退出后,这将被打印出来。我在析构函数中保留了一个 getchar() 来停止程序执行。否则我们将看不到这些行被打印出来。
0 Destroyed
1 Destroyed
2 Destroyed
谁能解释一下。
这有两个原因。
首先,push_back
有时会扩展向量的大小,重新分配 space,将所有对象复制到那里,并销毁旧对象。要摆脱这种重新分配的影响,您可以在循环之前添加 boxList.reserve(3)
,并获得更易于理解的输出:
0 Created
0 Destroyed
1 Created
1 Destroyed
2 Created
2 Destroyed
0 Destroyed
1 Destroyed
2 Destroyed
其次,push_back
复制您的本地 b
对象,然后 b
被销毁。这就解释了为什么上面输出中的每个对象在创建后很快就被销毁了。
最后三个调用当然是在程序退出时。
0 Created
创建本地Box b(0),复制到vector
0 Destroyed
并销毁
1 Created
现在创建了本地 Box b(1)
0 Destroyed
我认为此刻您的向量已重新分配并需要将对象复制到其新存储。原vector中index为0的对象在处理过程中被销毁
1 Destroyed
本地Box被销毁
2 Created
现在是索引为 2 的本地 Box 的时间
0 Destroyed
1 Destroyed
vector 再次被重新分配,需要销毁原始位置的对象
2 Destroyed
局部变量销毁
发生这种情况是因为 vector
正在使用 Box
的默认复制运算符创建对象的第二个实例,绕过您定义的构造函数。实际上,您确实有两个 Box
实例:b
和 boxList[i]
。
请注意,如果将赋值运算符显式定义为私有,编译器会发生什么情况:
class Box
{
public:
int x;
Box(int x);
~Box();
private:
Box& operator=( const Box& second ) {};
};
std 中大量的编译器错误是由于它在内部使用 operator= 来创建 b
对象的精确副本。
您将获得一个自动生成的复制构造函数。
如果你替换
Box b(i);
boxList.push_back(b);
与
boxList.emplace_back(i);
您可以进行现场施工。
如果您还使用 reserve(3)
(在添加项目之前),向量将不必四处移动(调用析构函数的另一个可能原因),那么应该恰好调用析构函数 3 次
其他答案正确地提到了隐式 copy constructor 的调用。要查看这些调用,只需将隐式复制构造函数替换为显式复制构造函数:
#include <iostream>
#include <vector>
class Box
{
public:
int x;
int copy_nr;
Box(int x);
Box(const Box& other); //copy constructor
~Box();
};
Box::~Box()
{
std::cout << x << " (copy " << copy_nr << ") Destroyed" << std::endl;
}
Box::Box(int x) : x(x), copy_nr(0)
{
std::cout << x << " (copy " << copy_nr << ") Created" << std::endl;
}
Box::Box(const Box& other) : x( other.x ), copy_nr( other.copy_nr + 1 )
{
std::cout << x << " (copy " << other.copy_nr <<") Copied"
" (creating copy " << copy_nr << ")" << std::endl;
}
int main()
{
std::vector<Box> boxList;
for (int i = 0; i < 3; i++)
{
Box b(i);
boxList.push_back(b);
}
return 0;
}
在我的机器上,这会产生:
0 (copy 0) Created
0 (copy 0) Copied (creating copy 1)
0 (copy 0) Destroyed
1 (copy 0) Created
1 (copy 0) Copied (creating copy 1)
0 (copy 1) Copied (creating copy 2)
0 (copy 1) Destroyed
1 (copy 0) Destroyed
2 (copy 0) Created
2 (copy 0) Copied (creating copy 1)
0 (copy 2) Copied (creating copy 3)
1 (copy 1) Copied (creating copy 2)
0 (copy 2) Destroyed
1 (copy 1) Destroyed
2 (copy 0) Destroyed
0 (copy 3) Destroyed
1 (copy 2) Destroyed
2 (copy 1) Destroyed
其他机器上的结果可能不同,因为 std::vector
的实现可以决定何时重新分配其内部数组。
我很困惑为什么析构函数被调用了这么多次。
#include <iostream>
#include <vector>
class Box
{
public:
int x;
Box(int x);
~Box();
};
Box::~Box()
{
std::cout << x << " Destroyed\n";
}
Box::Box(int x)
{
this->x = x;
std::cout << x << " Created\n";
}
int main()
{
std::vector<Box> boxList;
for (int i = 0; i < 3; i++)
{
Box b(i);
boxList.push_back(b);
}
return 0;
}
输出:
0 Created
0 Destroyed
1 Created
0 Destroyed
1 Destroyed
2 Created
0 Destroyed
1 Destroyed
2 Destroyed
main 退出后,这将被打印出来。我在析构函数中保留了一个 getchar() 来停止程序执行。否则我们将看不到这些行被打印出来。
0 Destroyed
1 Destroyed
2 Destroyed
谁能解释一下。
这有两个原因。
首先,push_back
有时会扩展向量的大小,重新分配 space,将所有对象复制到那里,并销毁旧对象。要摆脱这种重新分配的影响,您可以在循环之前添加 boxList.reserve(3)
,并获得更易于理解的输出:
0 Created
0 Destroyed
1 Created
1 Destroyed
2 Created
2 Destroyed
0 Destroyed
1 Destroyed
2 Destroyed
其次,push_back
复制您的本地 b
对象,然后 b
被销毁。这就解释了为什么上面输出中的每个对象在创建后很快就被销毁了。
最后三个调用当然是在程序退出时。
0 Created
创建本地Box b(0),复制到vector
0 Destroyed
并销毁
1 Created
现在创建了本地 Box b(1)
0 Destroyed
我认为此刻您的向量已重新分配并需要将对象复制到其新存储。原vector中index为0的对象在处理过程中被销毁
1 Destroyed
本地Box被销毁
2 Created
现在是索引为 2 的本地 Box 的时间
0 Destroyed 1 Destroyed
vector 再次被重新分配,需要销毁原始位置的对象
2 Destroyed
局部变量销毁
发生这种情况是因为 vector
正在使用 Box
的默认复制运算符创建对象的第二个实例,绕过您定义的构造函数。实际上,您确实有两个 Box
实例:b
和 boxList[i]
。
请注意,如果将赋值运算符显式定义为私有,编译器会发生什么情况:
class Box
{
public:
int x;
Box(int x);
~Box();
private:
Box& operator=( const Box& second ) {};
};
std 中大量的编译器错误是由于它在内部使用 operator= 来创建 b
对象的精确副本。
您将获得一个自动生成的复制构造函数。 如果你替换
Box b(i);
boxList.push_back(b);
与
boxList.emplace_back(i);
您可以进行现场施工。
如果您还使用 reserve(3)
(在添加项目之前),向量将不必四处移动(调用析构函数的另一个可能原因),那么应该恰好调用析构函数 3 次
其他答案正确地提到了隐式 copy constructor 的调用。要查看这些调用,只需将隐式复制构造函数替换为显式复制构造函数:
#include <iostream>
#include <vector>
class Box
{
public:
int x;
int copy_nr;
Box(int x);
Box(const Box& other); //copy constructor
~Box();
};
Box::~Box()
{
std::cout << x << " (copy " << copy_nr << ") Destroyed" << std::endl;
}
Box::Box(int x) : x(x), copy_nr(0)
{
std::cout << x << " (copy " << copy_nr << ") Created" << std::endl;
}
Box::Box(const Box& other) : x( other.x ), copy_nr( other.copy_nr + 1 )
{
std::cout << x << " (copy " << other.copy_nr <<") Copied"
" (creating copy " << copy_nr << ")" << std::endl;
}
int main()
{
std::vector<Box> boxList;
for (int i = 0; i < 3; i++)
{
Box b(i);
boxList.push_back(b);
}
return 0;
}
在我的机器上,这会产生:
0 (copy 0) Created
0 (copy 0) Copied (creating copy 1)
0 (copy 0) Destroyed
1 (copy 0) Created
1 (copy 0) Copied (creating copy 1)
0 (copy 1) Copied (creating copy 2)
0 (copy 1) Destroyed
1 (copy 0) Destroyed
2 (copy 0) Created
2 (copy 0) Copied (creating copy 1)
0 (copy 2) Copied (creating copy 3)
1 (copy 1) Copied (creating copy 2)
0 (copy 2) Destroyed
1 (copy 1) Destroyed
2 (copy 0) Destroyed
0 (copy 3) Destroyed
1 (copy 2) Destroyed
2 (copy 1) Destroyed
其他机器上的结果可能不同,因为 std::vector
的实现可以决定何时重新分配其内部数组。