代码之家  ›  专栏  ›  技术社区  ›  AndreasT

STD C++容器元素的破坏与插入行为

  •  5
  • AndreasT  · 技术社区  · 15 年前

    我做了以下小程序: (基本上是一个类,当它被创建、复制或销毁时会发生故障,而一个主类会执行其中的一些操作)

    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)不销毁它包含的元素? 它很适合共享资源的行为。 有人能告诉我为什么吗?

    加法 我把这个放在一个无休止的循环中,检查了mem的使用情况,它似乎没有产生 至少是膜漏。

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

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

    复制的逻辑数量。如果不这样做,它会重新分配和复制每一次向后推的整个向量(即使对于小向量,我也会发现这一点非常滞后)。 看看这个,我会考虑使用。保留更多并优化我的分配运算符,如hell:)

    添加:摘要

    1. 所有这些问题似乎都是VC++2005特有的。
    2. 如果两个容器的大小匹配,则我的实现在 元素而不是破坏旧的元素和复制新的元素 良好的实践。如果尺寸不同,则使用正常销毁和复制。
    3. 随着2005年的实施,一个人必须使用储备!否则表现糟糕且不符合标准。
    4. 这些黑盒子比我想象的黑得多。
    4 回复  |  直到 15 年前
        1
  •  4
  •   Ferdinand Beyer    15 年前

    如果复制了两次,我为什么还要费心传递一些东西作为参考呢?

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

    编译器的实现 push_back() 使用临时的额外副本。在我的机器上(Mac OS X上的GCC),在 PurthBook() (根据程序的输出)。

    这个复制发生在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的Bert创建了一个实例!
    佛祖的阿尔伯特实例被复制了!
    FOO的伯特实例被复制了!
    Foo的Bert被指派给了Albert!
    Foo的Bert实例被毁了!
    福的阿尔伯特实例被毁了!
    Foo的Bert实例被毁了!
    福的阿尔伯特实例被毁了!

        2
  •  3
  •   stribika    15 年前

    有一个自动生成的=运算符。当您执行v1=v2时,将使用该运算符。在这一点上,“阿尔伯特”的一个例子变成了“伯特”。尝试将此函数添加到foo:

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

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

        3
  •  1
  •   Vijay Mathew Chor-ming Lung    15 年前

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

        4
  •  1
  •   Kirill V. Lyadvinsky    15 年前

    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中,实现不是很有效。