Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Разрушение элемента контейнера std С++ и поведение вставки

Автор вопроса: AndreasTОтветов: 4

Я сделал следующую маленькую программу: (в основном класс, который проверяет, создается ли он, копируется или уничтожается, и основной, который делает некоторые из них)

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. Может кто-нибудь сказать мне, почему?

ДОПОЛНЕНИЕ Я поставил это в бесконечный цикл и проверил использование памяти, по крайней мере, похоже, что это не приводит к утечке памяти.

ДОПОЛНЕНИЕ Хорошо, мем не проблема, потому что он использует оператор = вместо копирования ctor, хорошо, спасибо. Когда я добавляю

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

имеет место логическое количество копий. без этого он перераспределяет и копирует весь вектор для каждого push_back (что я считаю довольно замедленным даже для небольших векторов). Глядя на это, я подумаю об использовании .reserve и оптимизирую свои операторы присваивания, черт возьми :)

ДОПОЛНЕНИЕ: ОБЗОР

  1. Все эти проблемы кажутся характерными для VC++2005.
  2. Если размеры двух контейнеров совпадают, моя реализация использует оператор = для элементов вместо уничтожения старых и копирования новых, что кажется разумной практикой. ЕСЛИ размеры различаются, используется обычное уничтожение и копирование.
  3. С реализацией 2005 года приходится использовать резерв! В противном случае ужасная и не соответствующая стандарту производительность.
  4. Эти черные ящики намного чернее, чем я думал.
  • Вы пытались скомпилировать это как релизную сборку? jalf 07.09.2009
  • Это легко попробовать самостоятельно, просто скопируйте и вставьте в пустой проект, добавьте iostream, vector и string и вперед. AndreasT 07.09.2009
  • Да, и релиз, и отладка дают одинаковый результат. AndreasT 07.09.2009
  • этот вывод не может соответствовать коду, что за вещь класса А ....? Pieter 07.09.2009
  • @Pieter: я очистил программу для публикации, но скопировал старый вывод. Я исправил это. Однако результат тот же. AndreasT 07.09.2009
  • Я никогда не использую VC++, поэтому не могу проверить это сам, но я был бы очень удивлен, если бы 3. С реализацией 2005 года приходилось использовать резерв! В противном случае ужасная и не соответствующая стандарту производительность. было бы правдой, есть что-нибудь, чтобы поддержать это? Pieter 07.09.2009

c++stlvisual-studio-2005

Ответы

Ответ 1 · рейтинг 4 · Ferdinand Beyer

Зачем мне вообще передавать что-то в качестве ссылки, если оно все равно копируется дважды?

Вы должны рассматривать типы контейнеров STL как черный ящик, который может копировать объекты, которые вы храните, так часто, как им нужно. Например, каждый раз, когда размер контейнера изменяется, все объекты будут скопированы.

Возможно, реализация вашего компилятора push_back() использует временную дополнительную копию. На моей машине (gcc в Mac OS X) во время push_back() нет дополнительных копий (согласно выводу вашей программы).

Эта копия происходит где-то в коде STL, а не в вашем конструкторе копирования (поскольку он использует ссылку).

Почему v1.operator=(other) не уничтожает содержащиеся в нем элементы?

Foo::operator= будет вызываться для экземпляра "albert" с экземпляром "bert" в качестве аргумента. Следовательно, здесь нет неявной операции уничтожения и копирования. Вы можете проверить это, предоставив собственную реализацию оператора:

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

Это производит следующий вывод на моей машине:

Экземпляр Albert of Foo создан!
Экземпляр Bert of Foo создан!
Экземпляр Albert of Foo скопирован!
Экземпляр Bert of Foo скопирован!
Экземпляр Bert of Foo присвоен Albert!
Экземпляр Bert of Фу уничтожен!
Экземпляр Альберт из Фу уничтожен!
Экземпляр Берт из Фу уничтожен!
Экземпляр Альберт из Фу уничтожен!

Ответ 2 · рейтинг 3 · stribika

Есть автоматически сгенерированный оператор =. Когда вы делаете v1 = v2, используется этот оператор. В этот момент один из экземпляров "albert" становится "bert". Попробуйте добавить эту функцию в Foo:

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

Это то же самое, что и автоматически сгенерированное, но выводит отладочное сообщение, чтобы вы могли видеть, что происходит.

Ответ 3 · рейтинг 1 · Vijay Mathew

"Двойное копирование" не происходит при компиляции с помощью GCC. Это должно быть связано с тем, как std::vector реализован в VC++.

Ответ 4 · рейтинг 1 · Kirill V. Lyadvinsky

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.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы