Смысл использования std::auto_ptr

В чем смысл auto_ptr? Посмотрите на этот код:

#include <iostream>
#include <memory>

class A
{
public:
    ~A()
    {
        std::cout << "DEST";
    };
};

void func(A* pa)
{
    std::cout << "A pointer";
}

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    //func(pA);  //compiler error here cannot convert parameter 1 from 'std::auto_ptr<_Ty>' to 'A *' 
    std::cout << "end";
}

int main(int argc, char* argv[])
{
    test();
    return 0;
}

Какой смысл использовать этот auto_ptr?

  • Он вызывает деструктор класса, когда выходит из области видимости, как обычная переменная инициализации класса (a).
  • Я не могу передать этот указатель функции с указателем класса (func)
  • Я не могу использовать указатель auto_ptr для A[] или char[], потому что auto_ptr вызывает удаление, а не delete[].

Единственное, что я думаю, это то, что мне не нужно писать «удалить», но какой смысл в указателе, если он будет уничтожен, когда я выйду за пределы области видимости. Я использую указатель для управления жизнью переменной.

Обычная инициализация переменной находится в стеке, а указатель в куче, но скажите мне, в чем смысл использования auto_ptr, а не обычного указателя?


person Likon    schedule 23.10.2011    source источник
comment
Пожалуйста, прочитайте немного о синтаксисе Markdown, это то, что мы используем здесь вместо HTML (поэтому нет необходимости в ‹br›). Это легко и приятно использовать, как только вы прочитаете, как это работает.   -  person Kos    schedule 23.10.2011
comment
Я отредактировал свой ответ на реальном примере. Пожалуйста, не пропустите часть о том, что auto_ptr устарел (и вместо этого следует использовать unique_ptr).   -  person Kos    schedule 23.10.2011


Ответы (5)


Единственное, что я думаю, это то, что мне не нужно писать удалить

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

какой смысл указателя, если он будет уничтожен, когда я выйду за пределы области видимости. Я использую указатель для управления жизнью переменной.

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

Ваши примеры надуманы, но вы все равно можете понять, почему это ценно. Конечно, вы, вероятно, не забудете вызвать delete в двухстрочной функции, которая вообще ничего не делает. В реальном приложении все становится сложнее.

Когда вы new скопируете несколько объектов, вы можете рассчитывать на то, что при выходе из функции объекты будут очищены. То же самое касается классов; когда экземпляр выходит за пределы области видимости, вы можете положиться на его деструктор, который позаботится об освобождении памяти за вас. Рассмотрим, что происходит, когда возникает исключение. Без auto_ptr вам нужно быть чертовски уверенным, что вы обрабатываете все возможные исключения и все возможные пути возврата, чтобы убедиться, что вы убираете за собой.

Также...

Обычная инициализация переменной находится в стеке, а указатель в куче

Не совсем. Указатель в этом случае имеет автоматическую продолжительность хранения, но то, на что он указывает, не имеет.

Причина, по которой ваш компилятор лает на то, что вы передаете auto_ptr, заключается в том, что функция не принимает auto_ptr. Вам нужно вызвать get(), чтобы передать сам указатель.

Тем не менее, вы также не должны использовать auto_ptr; используйте unique_ptr, если у вас есть доступ к компилятору C++ 0x, который вам следует.

person Ed S.    schedule 23.10.2011

Есть много причин использовать auto_ptr (или другие интеллектуальные указатели) для управления памятью. Давайте изменим этот код:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    if(SomeFunc())
      return;
    //func(pA);  //compiler error here cannot convert parameter 1 from 'std::auto_ptr<_Ty>' to 'A *' 
    std::cout << "end";
}

Если бы pA был не интеллектуальным указателем, у нас была бы утечка памяти. Но поскольку это так, мы знаем, что память будет правильно освобождена. Не о чем беспокоиться. Если бы SomeFunc выдало исключение, оно все равно было бы освобождено.

Чтобы решить вашу проблему с передачей указателя, сделайте следующее:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    func(pA.get());
    std::cout << "end";
}

Объект pA по-прежнему владеет памятью; func не должен удалять его и не должен сохранять его.

person Nicol Bolas    schedule 23.10.2011
comment
...и не должны держаться за него, так как он не будет действительным позже. - person Roger Lipscombe; 23.10.2011

Важный

Пожалуйста, помните, что у std::auto_ptr есть много недостатков, и обычно вместо этого следует использовать std::unique_ptr, если ваш компилятор предоставляет его. (Если это не так, обновите свой компилятор! :))

Теперь вернемся к вашему вопросу...


Какой смысл использовать этот auto_ptr? Он вызывает деструктор класса, когда выходит за пределы области действия, поскольку обычная переменная инициализации класса (a)

Точно. Причина для auto_ptr заключается в том, чтобы обеспечить строгую семантику владения, чтобы объект правильно уничтожался при уничтожении самого указателя.

Я не могу передать этот указатель функции с указателем класса (func)

Вы можете, вам нужно использовать get() для поиска необработанного указателя. Использование get() при передаче указателя на вызов функции означает, что функция не собирается брать на себя ответственность за объект (auto_ptr по-прежнему владеет им и ожидает, что он будет по-прежнему действителен после возврата из функции).

В качестве альтернативы вы можете использовать release() для указателя, чтобы получить необработанный указатель И указать, что auto_ptr больше не несет ответственности за владение объектом.

Я не могу использовать указатель auto_ptr для A[] или char[], потому что auto_ptr вызывает delete, а не delete[]

Да, это проблема. Это одна из причин, почему больше не используют auto_ptr с момента появления unique_ptr. Он делает то же самое, но безопаснее (= проще) в использовании и более универсален.

Единственное, что я думаю, это то, что мне не нужно писать «удалить», но какой смысл в указателе, если он будет уничтожен, когда я выйду за пределы области видимости.

Чтобы вы его не забыли :-) Или вы можете использовать auto_ptr (или лучше unique_ptr как член в объекте класса).

а скажи мне, какой смысл использовать auto_ptr, а не обычный указатель?

Короче говоря: многие указатели могут указывать на один объект. Существуют всевозможные интеллектуальные указатели, использующие систему типов для учета того, какой указатель владеет объектом (= отвечает за его освобождение).


Дополнительное примечание

Если у вас есть класс, который (может) владеть экземпляром другого объекта, вы просто пишете:

class X {
    // ...
    X() : ptr(new Type()) {}
    X(Type ptr) : ptr(ptr) {}
    // ...
    void setPtr(Type ptr2) { ptr.reset(ptr); }
    // ...
    std::unique_ptr<Type> ptr;
};

Если установлено ptr, то, например, деструктор unique_ptr позаботится об удалении объекта (если он есть). В методе setPtr функция reset() удалит старый экземпляр (если он есть) и установит элемент в новый предоставленный экземпляр (или нуль — это нормально).

Теперь сравните другую ситуацию:

class X {
   // ...
   X() : ptr(new Type()) {}
   X(Type ptr) : ptr(ptr) {}
   // ...
   void setPtr(Type ptr2) {delete ptr; ptr = ptr2;}
   // ...
   Type* ptr;
};

Такое же поведение? Нет! Потому что теперь, чтобы иметь безопасный код C++, вам дополнительно нужно написать деструктор для удаления ptr при уничтожении X.

Хорошо сейчас? НЕТ! Потому что, поскольку у вас есть общий деструктор, вам нужно свернуть (или заблокировать) свой собственный конструктор копирования и оператор присваивания, потому что в противном случае вы можете получить два экземпляра X, указывающих на один и тот же объект Type, и оба экземпляра здесь будут думать, что они владеют этим экземпляром, и оба когда-нибудь попытаются его удалить. Бум, нарушение прав доступа.

Unique_ptr не позволит вам неявно скопировать объект X вместе со строгой ссылкой на ptr, потому что unique_ptr не копируется (он считает, что это единственный unique_ptr для объекта, поэтому это единственный экземпляр интеллектуального указателя, ответственный за удаление это - но это нормально, если необработанные, не принадлежащие указатели указывают на него, если они не пытаются удалить то, что им не принадлежит!).

И это еще не все — unique_ptr не может быть скопирован, но у него есть готовый для вас конструктор перемещения и оператор присваивания перемещения! Следовательно, вы можете безопасно вернуть его из функций и так далее.

Вот как типобезопасность интеллектуальных указателей преобразуется в более безопасный код.

Золотое правило: старайтесь не писать «удалить» (если только вы не пишете свои собственные контейнеры или умные указатели). :)

person Kos    schedule 23.10.2011

Если вы можете позволить себе роскошь создавать все свои объекты в стеке, в auto_ptr нет необходимости. Но подумайте о:

  • возврат объекта из функции (вы не можете вернуть локальный объект)
  • убедиться, что объекты, возвращаемые из функций, будут либо получены вызывающей стороной, либо уничтожены при наличии исключений
  • агрегирование указателя на объект внутри класса (вам нужно убедиться, что деструктор удалит его, или вы можете просто сделать его auto_ptr, что предотвращает утечку памяти, и точка)
  • ...и многое другое

Если подумать, вам, вероятно, следует прочитать об этом Херба Саттера. Он знает больше, чем я. ;-)

person Jon    schedule 23.10.2011

Это правильный способ сделать это:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    func(pA.get());
    std::cout << "end";
}

в случае, если функция func выдает исключение, auto_ptr автоматически освобождает память, когда она выходит за пределы области видимости.

person BЈовић    schedule 23.10.2011
comment
Танк вам за все ответы. У меня есть еще один вопрос. При использовании auto_ptr или unique_ptr могу ли я управлять живым объектом, когда он выходит за пределы области видимости? Или указатель всегда будет удаляться, когда он выйдет за пределы области видимости? - person Likon; 30.10.2011