2009-03-31 1 views
3

Я не могу использовать shared_ptr в моем проекте, не подталкивание :(Можно ли хранить объекты класса с std :: auto_ptr в качестве его переменной-члена в std :: vector?

Итак, у меня есть класс, примерно аналогичный приведенному ниже:

class MyClass 
{ 
private: 
    std::auto_ptr<MyOtherClass> obj; 
}; 

Теперь я хочу, чтобы хранить экземпляры из выше класса в станд :: вектор. безопасно? Я read here, что это неправильно использовать зЬй :: auto_ptr с STL контейнеров. Относится ли это к моей ситуации здесь?

+0

Не знаете, что именно произошло с StackOverflow, кто-то прокомментировал, использовал ли я VS2008 или gcc4.1 +. Комментарий ушел, так и мой ответ на комментарий. Да, я на gcc 4.1.2. Спасибо за подсказку! У меня есть std :: tr1 :: shared_ptr! Проблема решена :) – Srikanth

ответ

2

Я отправил вопрос в качестве последующего ответа на этот ответ, см. Class containing auto_ptr stored in vector.

Ассистент вашего класса не имеет определяемого пользователем конструктора копирования, а затем нет, вероятно, (см. Ниже) небезопасно. Когда ваш класс будет скопирован (как это произойдет, когда он будет добавлен в вектор), будет использован конструктор копирования auto_ptr. У этого есть странное поведение transanferring собственности на вещь, скопированную в копию, и, таким образом, указатель на копирование теперь является нулевым.

Возможно, вы вряд ли захотите этого поведения, и в этом случае auto_ptr будет безопасным. Предполагая, что вы этого не сделаете, вы должны либо:

  • добавить конструктор копирования для управления копировального

Примечания этого не достаточно - увидеть последующий вопрос упомянутого выше для получения дополнительной информации.

или:

  • использование умнее, возможно, подсчет ссылок указатель, например, один из наддува смарт-указатели
+0

В примере в вопросе, я считаю, стоит отметить, что есть конструктор копирования, сгенерированный компилятором. –

+0

измененный ответ, чтобы покрыть это – 2009-03-31 08:58:58

+0

. Вы действительно НЕ хотите неявного поведения, поскольку оно нарушает базовое предположение STL для вашего типа: все копии объекта равны. Например. std :: sort WILL ломает ваш вектор, если копирование MyClass для использования в качестве точки опоры, переносит исходный объект в вектор из своего MyOtherClass. – MSalters

1

Копирование объекта MyClass вызовет либо позвонить оператору присваивания или конструктор копирования. Если они не перегружены для обработки auto_ptr <> необычным способом, они будут распространять вызов для копирования конструктора (или оператора присваивания) члену auto_ptr <>. Это может привести к проблемам, описанным в вопросе, который вы связали.

6

Это не безопасно, если контейнер будет скопирован. Оператор копирования MyClass instnace по умолчанию будет вызывать копирование для всех участников - и для члена auto_ptr тоже, и мы будем иметь такую ​​же ситуацию, какую вы описали в своем вопросе (сохранение auto_ptr в контейнере)

BTW: для избежания путаницы во время компиляции добавить

private: 
    MyClass& operator=(const MyClass&); 
    MyClass(const MyClass&); 

вывода ошибки компилятора, если вы постараетесь операторы использования копирования, это может спасти вас от часов отладки.

+0

Хорошая идея в целом, но в этом случае он не сможет добавить к вектору, если он сделает копию ctor & op = private. – 2009-03-31 08:57:03

+0

И именно поэтому это хорошая идея в этом случае. Он превращает возможную ошибку времени выполнения (разуплотнение нулевого указателя) в ошибку времени компиляции (MyClass не может быть скопирован) – MSalters

0

Это не сработает. auto_ptr не учитывает ссылки, что означает, что при первом вызове деструктора ваш указатель будет освобожден.

Используйте boost :: shared_ptr вместо этого.

1

Причина, по которой нецелесообразно инициировать вектор auto_pointer, заключается в том, что существует алгоритм: sort(), который будет делать копию одного объекта в вашем контейнере в стеке. (sort() реализует quicksort, который нуждается в «своде») И, следовательно, удаление его при выходе из scpope функции sort(). Как и любой алгоритм или ваша собственная функция, способная взять ваш контейнер в качестве параметра и скопировать один из его объектов в стек, приведет к этой проблеме в результате.

Ну, в вашем случае, вы должны убедиться, что ваш класс не ведет себя как auto_ptr, или убедитесь, что вы никогда не вызовете такую ​​функцию/алгоритм, который может удалить ваши базовые объекты. Первое решение лучше всего, согласно мне :)

Итак, ваш конструктор копий и ваш оператор аффектации также не должны отбрасывать свойство объекта-указателя.

Лучший способ добиться этого - обернуть интеллектуальный указатель boost вместо auto_ptr, чтобы сделать ваш контейнер безопасным при вызове такой функции/алгоритма.

44. Я считаю, результат применения алгоритма sort().

3

Как сказал Нил Баттерворт, auto_ptr, вероятно, не путь.

boost::shared_ptr ясно, но вы говорите, что вы не можете использовать boost.

Позвольте мне упомянуть, что вы можете загрузить boost, извлечь, что вам нужно для общего \ ptr, только используя bcp tool и использовать boost::shared_ptr. Это означало бы только несколько добавленных файлов hpp в вашем проекте. Я считаю, что это правильный путь.

+0

Benoit, я на GCC 4.1.2, не знал, что shared_ptr доступен в tr1. Кто-то дал намек; комментарий теряется после обслуживания StackOverflow. Теперь я использую std :: tr1 :: shared_ptr. – Srikanth

3

Это не действительный, чтобы иметь объект, который содержит auto_ptr в стандартном контейнере. Вы сталкиваетесь с неопределенным поведением. Две распространенные проблемы:

  • std :: vector <> :: resize копирует свой аргумент в каждый созданный элемент. Первая копия будет «успешной» (см. Ниже почему нет), но каждая последующая копия будет пустой, потому что скопированный элемент также пуст!
  • Если что-то во время перераспределения бросает, у вас могут быть некоторые элементы, скопированные (в новый буфер), - но отбрасываемая копия - и другие элементы нет, потому что push_back не должен иметь никаких эффектов, если генерируется исключение. Таким образом, некоторые из ваших элементов теперь пусты.

Как это все о неопределенном поведении, это не имеет большого значения. Но даже , если мы пытаемся придумать такое поведение, основанное на том, что мы считаем действительным, мы все равно потерпим неудачу. Все функции-члены, такие как push_back, resize и т. Д., Имеют ссылку на const, которая принимает объект типа T. Таким образом, ссылка типа T const& пытается скопировать в элементы вектора. Но неявно созданный оператор компоновщика копий/копий выглядит как T(T&), то есть для его копирования требуется объект non-const. Хорошие реализации стандартной библиотеки проверяют это, и при необходимости не скомпилируются.

До следующей версии на C++ вы должны жить с этим.Следующий будет поддерживать типы элементов, которые являются просто подвижным. То есть перемещенный объект не должен быть равен перемещенному объекту. Это позволит помещать потоки, указатели на перевод и ниток в контейнеры.

Посмотрите, что говорит стандарт для этого (17.4.3.6):

В некоторых случаях (замена функций, функций обработки, операций по типам используемых для создания экземпляров стандартных компонентов библиотеки шаблонов), С ++ Стандартная библиотека зависит от компонентов поставляемый программой C++. Если эти компоненты не соответствуют их требованиям, Стандарт не устанавливает никаких требований к реализации.

В частности, эффекты не определены в следующих случаях:

  • для типов, используемых в качестве аргументов шаблона при создании экземпляра шаблона компонента, если операции по типу не реализуют семантику применимых требований подпункта (20.1.5, 23.1, 24.1, 26.1).
+0

Извините, вы все еще не продемонстрировали UB. Пожалуйста, опубликуйте некоторые фактические C++ CODE (не ручные), которые делают это. Я очень уважаю ваши мнения, но я действительно думаю, что вы здесь не правы. – 2009-03-31 20:33:51

+0

сама природа UB заключается в том, что ее невозможно продемонстрировать :) –

+0

Не так! char * p = 0; * p = 'x'; // undefined behavior – 2009-03-31 20:53:09

1

Если вы хотите использовать класс, который использует auto_ptr в контейнере, вы можете просто предоставить копию-конструктор и оператор присваивания себе:

class MyClass 
{ 
private: 
    const std::auto_ptr<MyOtherClass> obj; // Note const here to keep the pointer from being modified. 

public: 
    MyClass(const MyClass &other) : obj(new MyOtherClass(*other.obj)) {} 
    MyClass &operator=(const MyClass &other) 
    { 
     *obj = *other.obj; 
     return *this; 
    } 
}; 

Но, как уже отмечалось, стандарт позволяет контейнеры делают копии и присваивания и предполагает, что содержащиеся классы будут вести себя определенным образом, что нарушает auto_ptr. Определив вышеописанные методы, вы можете создать класс, содержащий поведение auto_ptr. Даже если ваша реализация отлично работает с auto_ptrs, вы рискуете найти другую реализацию, которая не работает. Стандарт гарантирует только производительность и наблюдаемое поведение, а не реализацию.