Смысл использования std :: auto_ptr - PullRequest
2 голосов
/ 23 октября 2011

В чем смысл 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[].

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

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

Ответы [ 5 ]

5 голосов
/ 23 октября 2011

Единственная мысль - мне не нужно писать delete

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

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

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

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

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

Также ...

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

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

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

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

4 голосов
/ 23 октября 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 не должен удалять это и не должен хранить это.

3 голосов
/ 23 октября 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 not delete []

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

Единственная мысль - мне не нужно писать delete, но какой смысл в указателе, если он исчезнет, ​​когда я выйду из области видимости.

Так что вы не забудете это :-) Или вы можете использовать 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 не может быть скопирован, но у него есть конструктор перемещения и оператор присваивания перемещения, готовый для вас! Следовательно, вы можете безопасно вернуть его из функций и т. Д.

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

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

2 голосов
/ 23 октября 2011

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

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

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

1 голос
/ 23 октября 2011

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

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

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

...