我制作的类的析构函数在作用域结束之前被调用。我认为当我向其添加另一个元素时,它与向量中的重新分配有关。我如何超越这个问题?我希望只有当对象到达我的代码中的作用域末尾时才会调用析构函数。
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
~A() { cout << "destructor called\n"; }
};
int main ()
{
A one, two;
vector<A> vec;
cout << "push_back one" << endl;
vec.push_back(one);
cout << "push_back two" << endl;
vec.push_back(two);
//destructor gets called here
system("pause");
return 0;
} //while i want it to be called here
答案 0 :(得分:3)
代码中的打印输出不会显示完整的图片:为不同的对象调用析构函数。
看看这个修改后的代码。我添加了地址的打印输出,并登录了其他一些关键位置,例如调用赋值运算符和复制构造函数。
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
A() {cout << "constructed " << (void*)this << "\n";}
A(const A& a) {cout << "copied " << (void*)(&a) << " to " << (void*)this << "\n"; }
A& operator =(const A& a) {cout << "assigned " << (void*)(&a) << " to " << (void*)this << "\n"; }
~A() { cout << "destructor called for " << (void*)this << "\n"; }
};
int main ()
{
A one, two;
vector<A> vec;
cout << "push_back one" << endl;
vec.push_back(one);
cout << "push_back two" << endl;
vec.push_back(two);
//destructor gets called here
return 0;
}
它产生以下输出(demo):
constructed 0xbff229b2
constructed 0xbff229b3
push_back one
copied 0xbff229b2 to 0x970c008
push_back two
copied 0xbff229b3 to 0x970c019
copied 0x970c008 to 0x970c018
destructor called for 0x970c008
destructor called for 0x970c018
destructor called for 0x970c019
destructor called for 0xbff229b3
destructor called for 0xbff229b2
您可以看到,对象one
和two
(0xbff229b2
和0xbff229b3
)直到最后,当它们超出范围时才会被销毁。它的副本在向量调整大小时会被破坏。
答案 1 :(得分:2)
根据C ++标准§23.3.6:
向量具有特定容量,意味着
向量可以容纳的元素总数,无需重新分配
当你push_back
增加一个元素时,这会有效地将容器大小增加一,
如果新大小大于旧容量
,则会导致重新分配
重新 - 分配意味着您 de - 分配(即销毁)向量中的元素,然后再次分配(即构建)它们。
请注意,在您的情况下,one
的原始two
和A
实例不会被销毁,而是存储在向量中的副本。
程序员需要对引用和迭代器更加小心,因为
重新分配使引用序列中元素的所有引用,指针和迭代器无效。
正如其他人所说,reserve()
将有助于防止这种情况,因为
在调用reserve()之后发生的插入期间不会发生重新分配 直到插入会使向量的大小大于capacity()的值。
答案 2 :(得分:1)
正如评论所示,您应该使用std::vector<>::reserve(size_t)
。
您所看到的是实施定义。大多数实现在增长时大约是保留分配大小的两倍。这是为了避免在大小增加时进行许多分配和复制。
预约仅向集合建议您需要 n 元素。如果实现选择遵守请求,则实现将现有值重新分配并移动/复制到新分配,该分配足够大以容纳您请求的元素数量。现在你可以在没有昂贵的重新分配的情况下推迟。当你很好地了解最终尺寸时,正确保留可以节省许多重新分配的容量。
在这种情况下保留会避免调整大小,以及破坏您看到的临时值。
您可能会看到析构函数,因为集合正在调整大小。当你第二次推回时,它会重新分配。
如果永远不要销毁对象,则应考虑另一种集合类型或存储位置,因为程序的当前形式依赖于实现定义的行为(即标准的向量规范不是做出你需要的保证。)