std c ++容器元素销毁和插入行为

时间:2009-09-07 13:16:41

标签: c++ visual-studio-2005 stl

我做了以下小程序: (基本上是一个类,如果它被创建,复制或销毁,并且主要执行其中一些)

class Foo
{
public:
 Foo(string name): _name(name)
 {
  cout << "Instance " << _name << " of Foo created!" << std::endl;
 };
 Foo(const Foo& other): _name(other._name)
 {
  cout << "Instance " << _name << " of Foo copied!" << std::endl;
 };

 ~Foo()
 {
  cout << "Instance " << _name << " of Foo destroyed!" << std::endl;
 }
 string _name;
};



int main( int argc, char**argv)
{
 Foo albert("Albert");
 Foo bert("Bert");
 {
  vector<Foo> v1, v2;
  system("PAUSE");  

  v1.push_back(albert);
  system("PAUSE");

  v2.push_back(bert);
  system("PAUSE");

  v1 = v2;
  system("PAUSE");  
 }
  system("PAUSE");
}

输出如下:

Instance Albert of class Foo created!
Instance Bert of class Foo created!
Press any key...
Instance Albert of class Foo copied!    
Instance Albert of class Foo copied!     // why another copy?
Instance Albert of class Foo destroyed!  // and destruction?
Press any key...
Instance Bert of class Foo copied!
Instance Bert of class Foo copied!
Instance Bert of class Foo destroyed!
Press any key...                      // v1=v2 why did the albert instance not get destroyed?
Press any key...                       
Instance Bert of class A destroyed!
Instance Bert of class A destroyed!
Press any key...                       // there's still an albert living in the void

这让我非常奇怪。 如果它被复制两次,为什么我甚至不愿意传递一些东西作为参考呢? 为什么v1.operator =(other)不会破坏它包含的元素? 它很适合shared_ptr的行为。 有人可以告诉我为什么吗?

ADDITION 我把它放在一个无限循环中并检查内存使用情况,它似乎没有产生 至少是一个内存泄漏。

ADDITION 好吧,mem不是问题,因为它使用operator =而不是copy ctor,好的,谢谢。 当我添加

v1.reserve(10);
v2.reserve(10);

发生逻辑份数。没有它,它会为每一个push_back重新分配和复制整个向量,(即使对于小向量,我发现它也很迟钝)。 看看这个,我会考虑使用.reserve更多并优化我的赋值运算符如地狱:)

附加:摘要

  1. 所有这些问题似乎都特定于VC ++ 2005。
  2. 如果两个容器的大小匹配,我的实现使用operator = on 似乎是元素而不是摧毁旧元素并复制新元素 健全的做法。如果尺寸不同,则使用正常的破坏和复制。
  3. 随着2005年的实施,人们必须使用储备!否则就是糟糕而不符合标准的表现。
  4. 这些黑匣子比我想象的要黑得多。

4 个答案:

答案 0 :(得分:4)

  

如果它被复制两次,为什么我甚至不愿意传递一些东西作为参考?

您应该将STL容器类型视为黑盒,可以根据需要随时复制存储的对象。例如,每次调整容器大小时,都会复制所有对象。

编译器的push_back()实现可能使用临时额外副本。在我的机器上(Mac OS X上的gcc),push_back()期间没有额外的副本(根据您的程序输出)。

此副本发生在STL代码中的某个位置,而不是在您的复制构造函数中(因为它使用了引用)。

  

为什么v1.operator =(other)不会破坏它包含的元素?

Foo::operator=将以“bert”实例作为参数调用“albert”实例。因此,这里没有隐式的销毁和复制操作。您可能希望通过为运营商提供自己的实现来验证这一点:

Foo& operator=(const Foo& other) {
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl;
    return *this;
}

这会在我的机器上产生以下输出:

  

Foo的阿尔伯特创建了!   Foo的实例Bert创建了!
  Foo的阿尔伯特实例被复制了!
  Foo的实例Bert被复制了!
  Foo的实例Bert分配给Albert!   Foo的实例Bert被摧毁!
  Foo阿尔伯特被毁坏了!
  Foo的实例Bert被摧毁!
  Foo的阿尔伯特实例被毁了!

答案 1 :(得分:3)

有一个auto generated =运算符。当你使用v1 = v2运算符时。此时,其中一个“albert”实例变为“bert”。尝试将此功能添加到Foo:

Foo& operator = (const Foo& rval) {
    cout << _name << " = " << rval._name << endl;
    _name = rval._name;
    return *this;
}

这与自动生成的相同,但打印出调试消息,以便您可以看到正在发生的事情。

答案 2 :(得分:1)

使用GCC编译时不会发生“双重复制”。这必须特定于在VC ++中实现std :: vector的方式。

答案 3 :(得分:1)

Visual Studio 2008为我提供了以下输出:

Instance Albert of Foo created!
Instance Bert of Foo created!
Press any key to continue . . .
Instance Albert of Foo copied!
Press any key to continue . . .
Instance Bert of Foo copied!
Press any key to continue . . .
Press any key to continue . . .  << here auto-generated operator= doing its job
Instance Bert of Foo destroyed!
Instance Bert of Foo destroyed!  << this is Albert was originally 
Press any key to continue . . .

似乎std::vector实施在VS2005中效果不是很好。