以下是代码:
#include <vector>
#include <iostream>
class A
{
public:
A() { std::cout << __FUNCTION__ << "\n"; }
~A() { std::cout << __FUNCTION__ << "\n"; }
A& operator=(const A&) { std::cout << __FUNCTION__ << "\n"; return *this;}
};
int main(int argc, char* argv[])
{
std::vector<A> as;
A a;
as.push_back(a);
as.push_back(a);
return 0;
}
这是我得到的输出:
A::A
A::~A
A::~A
A::~A
A::~A
据我所知,第一行的输出来自于创建“a”时对c-tor的调用。其中一个对d-tor的调用也属于a。 对A ::〜A()的其他三个调用怎么样,它们来自哪里? 为什么有更多的呼叫到d-tor比呼叫c-tor? 容器在将副本添加到元素时如何克隆'a'? 最后,是输出实现定义还是有其他可能的输出?
答案 0 :(得分:3)
您需要添加复制构造函数:
A( const A& ) { std::cout << __FUNCTION__ << "\n"; }
与所有其他标准库容器一样,该向量存储副本 - 这些副本是使用复制构造函数创建的。但是您应该知道,有几个地方允许编译器忽略复制构造,甚至制作额外的副本,因此您的输出可能与您期望的完全不同。
答案 1 :(得分:2)
要了解会发生什么,您在A:
中缺少一种方法A(const A&) { std::cout << __FUNCTION__ << "(const A&)\n"; }
然后你看到输出:
A()
A(const A&)
A(const A&)
A(const A&)
~A
~A
~A
~A
对于每个push_back,向量分配一个新的连续数组,复制旧内容并销毁它。如果算一算,第一个拷贝构造函数用于第一个push_back,第二个和第三个用于下一个push_back。第一个析构函数用于第二个push_back,两个用于销毁向量,最后一个用于销毁变量a。
顺便说一句,这是完全实现定义的,因为它允许按块分配,这可以防止相当多的复制/破坏。您甚至可以使用vector::reserve(size_type n)
来自行调整块。
答案 2 :(得分:0)
我确信您已经意识到您正在堆栈中创建“A”对象,然后使用复制构造函数将它们复制到数组中(正如其他答案所示)。
如果您的A对象是包含他们拥有的数据的真实数据对象,或者是难以复制的复杂状态,您可能需要考虑在数组中存储指针而不是直接存储对象。你需要自己管理内存,但这是值得考虑的一点。
int main(int argc, char* argv[])
{
std::vector<A*> as;
A *a = new A();
as.push_back(a);
as.push_back(a);
return 0;
}
你会看到输出:
A()
由于只创建了一个对象实例,而该数组只是存储指针的副本。显然,在某些时候你需要通过它的一个指针删除对象本身,以确保最终获得内存。