我不知道为什么在下一个时间点调用vector中对象的破坏。
class Something
{
public:
Something() {}
~Something() { cout << "destruction called" << endl; }
};
int main()
{
std::vector<Something> vec;
Something sth1 = Something();
Something sth2 = Something();
vec.push_back(sth1);
vec.push_back(sth2);
vec.clear();
}
在我按下sth2后,调用sth1的破坏。为什么?不应该保留在vec [0]中吗?
答案 0 :(得分:10)
因为vector
必须调整其容量才能存储两个元素而不是一个元素。它分配一个新缓冲区,它将旧缓冲区复制到新缓冲区,删除旧缓冲区,然后添加新对象。
答案 1 :(得分:2)
向量在运行中分配内存(作为连续的块,即数组)以保存放入其中的对象。由于事先并不知道它会增长多少,因此随着你添加更多元素,它会逐渐增加容量。
每次必须增长以适应新元素时,它必须分配一个足够大的新数组来存储所有内容(通常是旧数组的两倍,以避免过于频繁地调整大小);然后它必须将旧数组中的所有元素复制到新数组。然后释放旧数组,销毁它包含的对象。
vec[0]
将包含与sth1
完全相同的对象,而不是同一个对象(它将是在调整大小期间使用复制构造函数创建的副本矢量)。
另请注意,push_back()
始终复制正在推送的元素(它不保留引用);这允许它所持有的对象在它们的生命周期中持久存在于堆栈中。因此,在main()
的末尾,将对析构函数进行四次调用:一个用于向量中的两个对象中的每一个,一个用于sth1
和sth2
,因为它们被弹出堆栈。
答案 2 :(得分:1)
1) Something sth1 = Something();
2) Something sth2 = Something();
3) vec.push_back(sth1);
4) vec.push_back(sth2);
5) vec.clear();
1)你正在堆栈上创建sth1
- 它的生命周期将一直存在,直到堆栈帧被解除,main()
退出。允许编译器 (标准12.8.15)忽略赋值,使构造等同于:
Something sth1;
但是,编译器也可以更盲目地跟踪你的代码并且可能效率低下:
1a) create a temporary Something()
1a) copy-construct sth1 on the stack, lifetime as per main(), copying the value of the temporary
1c) destroy the temporary
总结:不要担心赋值:与C#或Java不同,对象是在C ++中隐式构造的。
在3) - 可以期望vec.push_back(sth1)
在堆上分配一些内存,然后使用与sth1
相同的值复制构造其中的对象。请注意,这不是不是 sth1
本身,而是执行sth1
时push_back
的值的副本。这些副本的更新以及sth1
和sth2
的更新都是独立的。
在4)向量可能需要也可能不需要调整其堆内存的大小以便为sth1的副本腾出空间:如果它这样做,那么[0]
处的现有值 - 这是从sth1复制构造的但不实际上sth1
,将被复制到新的内存区域,然后将在即将释放的内存区域中调用析构函数。
在5)sth1
和sth2
的副本被销毁
5之后,sth1和sth2自己超出范围作为主要退出,并调用它们的析构函数。
如果您希望看到这种情况发生,我建议您为班级添加更好的跟踪:
struct Something
{
Something() { std::cout << "Something(this " << (void*)this << ")\n"; }
~Something() { std::cout << "~Something(this " << (void*)this << ")\n"; }
Something(const Something& rhs)
{ std::cout << "Something(this " << (void*)this
<< ", rhs " << (void*)rhs << ")\n"; }
Something& operator=(const Something& rhs)
{ std::cout << "operator=(this " << (void*)this
<< ") = " << (void*)rhs << '\n'; }
};
您还可以在每个(void*)&vec[0]
之后打印push_back
。
从您的尝试中退后一步,看看是否/如何消除复制:
vector<Something*>
,然后push_back(&sth1)
和&sth2
sth1
和sth2
的生命周期(这是周围范围的生命周期 - 这里是main()
本身的生命周期)跨越指针的生命周期就可以了。可以被解除引用sth1
和sth2
对象的指针 - 而不是使用push_back()
时生成的副本 - 表示对sth1
和{{的任何更新1}}将通过指针显示,通过指针的更新实际上会影响sth2
和sth1
如果您希望向量已知的对象的生命周期超出基于堆栈的sth2
和sth1
周围范围所暗示的范围,您可以:
sth2
sth1
和sth2