Понимание значения термина и концепции - RAII (Resource Acquisition is Initialization)

110

Не могли бы вы, разработчики C ++, дать нам хорошее описание того, что такое RAII, почему он важен и может ли он иметь какое-либо отношение к другим языкам?

Я сделать немного знаю. Я считаю, что это означает «Приобретение ресурсов - это инициализация». Однако это имя не согласуется с моим (возможно, неправильным) пониманием того, что такое RAII: у меня создается впечатление, что RAII - это способ инициализации объектов в стеке, так что, когда эти переменные выходят за пределы области видимости, деструкторы автоматически вызывается, вызывая очистку ресурсов.

Так почему же это не называется «использование стека для запуска очистки» (UTSTTC :)? Как оттуда добраться до «РАИИ»?

И как вы можете сделать что-то в стеке, что приведет к очистке того, что живет в куче? Кроме того, есть случаи, когда вы не можете использовать RAII? Вы когда-нибудь мечтали о вывозе мусора? По крайней мере, сборщик мусора, который вы могли бы использовать для одних объектов, позволяя управлять другими?

Спасибо.

Чарли Флауэрс
источник
27
UTSTTC? Мне это нравится! Это намного более интуитивно понятно, чем RAII. RAII это плохо по имени, я сомневаюсь , что C ++ программист будет оспаривать это. Но изменить это непросто. ;)
jalf
10
Вот мнение Страуструпа по этому поводу: groups.google.com/group/comp.lang.c++.moderated/msg/…
sbi
3
@sbi: Во всяком случае, +1 к вашему комментарию просто для исторического исследования. Я считаю, что точка зрения автора (Б. Страуструпа) на название концепции (RAII) достаточно интересна, чтобы иметь собственный ответ.
paercebal
1
@paercebal: Историческое исследование? Теперь ты заставил меня почувствовать себя очень старым. :(Тогда я читал всю цепочку писем и даже не считал себя новичком в C ++!
sbi
3
+1, я собирался задать тот же вопрос, рад, что я не единственный, кто понимает концепцию, но не понимаю названия. Похоже, это должно было называться RAOI - Resource Acquisition On Initialization.
laurent

Ответы:

132

Так почему же это не называется «использование стека для запуска очистки» (UTSTTC :)?

RAII говорит вам, что делать: получить свой ресурс в конструкторе! Я бы добавил: один ресурс, один конструктор. UTSTTC - лишь одно из применений этого, RAII - намного больше.

Управление ресурсами - отстой. Здесь ресурс - это все, что требует очистки после использования. Исследования проектов на многих платформах показывают, что большинство ошибок связано с управлением ресурсами - и это особенно плохо в Windows (из-за множества типов объектов и распределителей).

В C ++ управление ресурсами особенно сложно из-за комбинации исключений и шаблонов (стиль C ++). Чтобы заглянуть под капот, см. GOTW8 ).


C ++ гарантирует, что деструктор вызывается тогда и только тогда, когда конструктор завершился успешно. Опираясь на это, RAII может решить множество неприятных проблем, о которых средний программист может даже не подозревать. Вот несколько примеров помимо «мои локальные переменные будут уничтожены, когда я вернусь».

Начнем с чрезмерно упрощенного FileHandleкласса, использующего RAII:

class FileHandle
{
    FILE* file;

public:

    explicit FileHandle(const char* name)
    {
        file = fopen(name);
        if (!file)
        {
            throw "MAYDAY! MAYDAY";
        }
    }

    ~FileHandle()
    {
        // The only reason we are checking the file pointer for validity
        // is because it might have been moved (see below).
        // It is NOT needed to check against a failed constructor,
        // because the destructor is NEVER executed when the constructor fails!
        if (file)
        {
            fclose(file);
        }
    }

    // The following technicalities can be skipped on the first read.
    // They are not crucial to understanding the basic idea of RAII.
    // However, if you plan to implement your own RAII classes,
    // it is absolutely essential that you read on :)



    // It does not make sense to copy a file handle,
    // hence we disallow the otherwise implicitly generated copy operations.

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;



    // The following operations enable transfer of ownership
    // and require compiler support for rvalue references, a C++0x feature.
    // Essentially, a resource is "moved" from one object to another.

    FileHandle(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
    }

    FileHandle& operator=(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
        return *this;
    }
}

Если построение не удается (за исключением), никакая другая функция-член - даже деструктор - не вызывается.

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

Теперь давайте посмотрим на временные объекты:

void CopyFileData(FileHandle source, FileHandle dest);

void Foo()
{
    CopyFileData(FileHandle("C:\\source"), FileHandle("C:\\dest"));
}

Есть три случая ошибок, которые необходимо обработать: ни один файл нельзя открыть, можно открыть только один файл, можно открыть оба файла, но скопировать файлы не удалось. В реализации без RAII Fooпришлось бы обрабатывать все три случая явно.

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

Теперь давайте объединим несколько объектов:

class Logger
{
    FileHandle original, duplex;   // this logger can write to two files at once!

public:

    Logger(const char* filename1, const char* filename2)
    : original(filename1), duplex(filename2)
    {
        if (!filewrite_duplex(original, duplex, "New Session"))
            throw "Ugh damn!";
    }
}

Конструктор Loggerзавершится ошибкой, если originalконструктор не работает (потому что filename1не может быть открыт), duplexконструктор не работает (потому что filename2не может быть открыт) или запись в файлы внутри Loggerтела конструктора не выполняется. В любом из этих случаев Loggerдеструктор не будет вызван, поэтому мы не можем полагаться на Loggerдеструктор для освобождения файлов. Но если originalбыл построен, его деструктор будет вызываться во время очистки Loggerконструктора.

RAII упрощает очистку после частичного строительства.


Отрицательные моменты:

Отрицательные моменты? Все проблемы решаются с помощью RAII и умных указателей ;-)

RAII иногда бывает громоздким, когда вам нужно отложенное получение, помещая агрегированные объекты в кучу.
Представьте, что регистратору нужен файл SetTargetFile(const char* target). В этом случае дескриптор, который по-прежнему должен быть членом Logger, должен находиться в куче (например, в интеллектуальном указателе, чтобы должным образом инициировать уничтожение дескриптора).

На самом деле, я никогда не хотел вывоза мусора. Когда я пишу на C #, я иногда чувствую момент блаженства, что мне просто не нужно заботиться, но гораздо больше я скучаю по всем крутым игрушкам, которые можно создать посредством детерминированного разрушения. (использование IDisposableпросто не режет.)

У меня была одна особенно сложная структура, которая могла бы выиграть от GC, где «простые» интеллектуальные указатели будут вызывать циклические ссылки на несколько классов. Мы запутались, тщательно сбалансировав сильные и слабые стороны, но каждый раз, когда мы хотим что-то изменить, мы должны изучить большую диаграмму отношений. GC мог бы быть лучше, но некоторые из компонентов содержали ресурсы, которые следует выпустить как можно скорее.


Примечание к образцу FileHandle: он не был задуман как полный, это всего лишь образец, но оказался неверным. Спасибо Йоханнесу Шаубу за указание и FredOverflow за превращение его в правильное решение C ++ 0x. Со временем я остановился на описанном здесь подходе .

peterchen
источник
1
+1 За то, что указывает, что GC и ASAP не связаны. Болит не часто, но когда это не так просто поставить диагноз: /
Matthieu M.
10
В частности, одно предложение, которое я упустил из виду при предыдущих чтениях. Вы сказали, что «RAII» говорит вам: «Соберите ресурсы внутри конструкторов». Это имеет смысл и является почти дословным перефразированием RAII. Теперь я понял это даже лучше (я бы проголосовал за вас еще раз, если бы мог :)
Чарли Флауэрс
2
Одним из основных преимуществ GC является то, что структура распределения памяти может предотвратить создание висящих ссылок в отсутствие «небезопасного» кода (если «небезопасный» код разрешен, конечно, структура не может ничего предотвратить). GC также часто превосходит RAII при работе с совместно используемыми неизменяемыми объектами, такими как строки, которые часто не имеют четкого владельца и не требуют очистки. К сожалению, многие фреймворки не стремятся объединить GC и RAII, поскольку большинство приложений будут иметь сочетание неизменяемых объектов (где GC будет лучше всего) и объектов, нуждающихся в очистке (где RAII лучше всего).
supercat
@supercat: Мне вообще нравится GC, но он работает только для ресурсов, которые GC "понимает". Например, .NET GC не знает стоимости COM-объектов. При простом создании и уничтожении их в цикле он с радостью позволит приложению упасть в землю в отношении адресного пространства или виртуальной памяти - что бы ни случилось раньше - даже не задумываясь о том, чтобы, возможно, выполнить сборку мусора. --- кроме того, даже в среде с идеальной сборкой мусора мне все еще не хватает силы детерминированного разрушения: вы можете применить тот же шаблон к другим искусствам, например, показывая элементы пользовательского интерфейса в определенных условиях.
peterchen
@peterchen: Я думаю, что во многих мышлениях, связанных с ООП, отсутствует концепция владения объектами. Отслеживание владения часто явно необходимо для объектов с ресурсами, но также часто необходимо для изменяемых объектов без ресурсов. В общем, объекты должны инкапсулировать свое изменяемое состояние либо в ссылках на возможно совместно используемые неизменяемые объекты, либо в изменяемые объекты, исключительными владельцами которых они являются. Такое исключительное владение не обязательно подразумевает исключительный доступ на запись, но если он Fooвладеет Barи Bozизменяет его, ...
supercat
42

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

0. RAII касается областей применения

RAII касается обоих:

  1. получение ресурса (независимо от того, какой ресурс) в конструкторе и отмена его получения в деструкторе.
  2. выполнение конструктора при объявлении переменной и автоматическое выполнение деструктора, когда переменная выходит за пределы области видимости.

Другие уже ответили об этом, поэтому я не буду вдаваться в подробности.

1. При кодировании на Java или C # вы уже используете RAII ...

MONSIEUR JOURDAIN: Что? Когда я говорю: «Николь, принеси мне тапочки и дай мне ночной колпак» - это проза?

МАСТЕР ФИЛОСОФИИ: Да, сэр.

MONSIEUR JOURDAIN: Более сорока лет я говорю прозой, ничего не зная о ней, и я очень благодарен вам за то, что вы меня этому научили.

- Мольер: Джентльмен среднего класса, Акт 2, Сцена 4

Как и мсье Журден с прозой, C # и даже Java люди уже используют RAII, но скрытыми способами. Например, следующий код Java (который на C # написан таким же образом, заменяясь synchronizedна lock):

void foo()
{
   // etc.

   synchronized(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

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

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

Преимущество C ++ над Java и C # в том, что с помощью RAII можно сделать все, что угодно. Например, нет прямого встроенного эквивалента synchronizedили lockв C ++, но мы все еще можем их иметь.

В C ++ это было бы написано:

void foo()
{
   // etc.

   {
      Lock lock(someObject) ; // lock is an object of type Lock whose
                              // constructor acquires a mutex on
                              // someObject and whose destructor will
                              // un-acquire it 

      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

который можно легко написать на языке Java / C # (с использованием макросов C ++):

void foo()
{
   // etc.

   LOCK(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

2. RAII имеет альтернативное использование

БЕЛЫЙ КРОЛИК: [поет] Я опаздываю / Я опаздываю / На очень важное свидание. / Нет времени говорить «Привет». / Прощай. / Я опаздываю, я опаздываю, я опаздываю.

- Алиса в стране чудес (версия Диснея, 1951)

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

Например, вы можете написать объект счетчика (я позволил это в качестве упражнения) и использовать его, просто объявив его переменную, как был использован объект блокировки выше:

void foo()
{
   double timeElapsed = 0 ;

   {
      Counter counter(timeElapsed) ;
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

что, конечно же, может быть написано, опять же, способом Java / C # с использованием макроса:

void foo()
{
   double timeElapsed = 0 ;

   COUNTER(timeElapsed)
   {
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

3. Почему не хватает C ++ finally?

[SHOUTING] Это последний обратный отсчет!

- Европа: последний отсчет времени (извините, у меня не было цитат, здесь ... :-)

Предложение finallyиспользуется в C # / Java для обработки удаления ресурсов в случае выхода из области действия (либо посредством returnисключения, либо сгенерированного исключения).

Проницательные читатели спецификации заметят, что в C ++ нет пункта finally. И это не ошибка, потому что C ++ в этом не нуждается, поскольку RAII уже обрабатывает удаление ресурсов. (И поверьте мне, написать деструктор C ++ намного проще, чем написать правильное предложение finally в Java или даже правильный метод Dispose в C #).

Тем не менее, иногда finallyоговорка была бы крутой. Можем ли мы сделать это на C ++? Да мы можем! И снова с альтернативным использованием RAII.

Вывод: RAII - это больше, чем философия в C ++: это C ++

RAII? ЭТО С ++ !!!

- Возмущенный комментарий разработчика C ++, беззастенчиво скопированный малоизвестным королем Спарты и его 300 друзьями

Когда вы достигнете определенного уровня опыта в C ++, вы начнете мыслить в терминах RAII , в терминах автоматического выполнения конструкторов и деструкторов .

Вы начинаете думать в терминах областей , а также {и }персонажи становятся одними из самых важных в вашем коде.

И почти все подходит с точки зрения RAII: безопасность исключений, мьютексы, соединения с базой данных, запросы к базе данных, соединение с сервером, часы, дескрипторы ОС и т. Д., И, наконец, что не менее важно, память.

Часть базы данных не является незначительной, так как, если вы согласитесь заплатить цену, вы даже можете писать в стиле « транзакционного программирования », выполняя строки и строки кода, пока в конце концов не решите, хотите ли вы зафиксировать все изменения или, если это невозможно, возврат всех изменений (при условии, что каждая строка удовлетворяет хотя бы Строгой гарантии исключения). (см. вторую часть статьи Herb's Sutter о транзакционном программировании).

И как в пазле, все подходит.

RAII - это неотъемлемая часть C ++, без него C ++ не мог бы быть C ++.

Это объясняет, почему опытные разработчики C ++ так влюблены в RAII и почему RAII - это первое, что они ищут, пробуя другой язык.

И это объясняет, почему сборщик мусора, хотя сам по себе является великолепной технологией, не так впечатляет с точки зрения разработчика C ++:

  • RAII уже обрабатывает большинство случаев, обрабатываемых GC
  • Сборщик мусора лучше, чем RAII, справляется с циклическими ссылками на чистые управляемые объекты (смягчается за счет разумного использования слабых указателей)
  • Тем не менее, сборщик мусора ограничен памятью, тогда как RAII может обрабатывать любые виды ресурсов.
  • Как описано выше, RAII может делать гораздо больше ...
paercebal
источник
Поклонник Java: я бы сказал, что сборщик мусора намного полезнее, чем RAII, поскольку он обрабатывает всю память и освобождает вас от многих потенциальных ошибок. С помощью GC вы можете создавать циклические ссылки, возвращать и сохранять ссылки, и это трудно ошибиться (сохранение ссылки на предположительно недолговечный объект увеличивает время его существования, что является своего рода утечкой памяти, но это единственная проблема) . Обработка ресурсов с помощью GC не работает, но большинство ресурсов в приложении имеют тривиальный жизненный цикл, и несколько оставшихся не имеют большого значения. Я бы хотел, чтобы у нас были и GC, и RAII, но это кажется невозможным.
maaartinus
16

Посмотри пожалуйста:

Программисты других языков, помимо C ++, используют, знают или понимают RAII?

RAII и интеллектуальные указатели в C ++

Поддерживает ли C ++ блоки «finally»? (И что это за RAII, о котором я все время слышу?)

RAII против исключений

и т.д..

Митч Уит
источник
1
Некоторые из них соответствуют моему вопросу, но поиск не нашел их, равно как и список «связанных вопросов», который появляется после ввода нового вопроса. Спасибо за ссылки.
Чарли Флауэрс,
1
@Charlie: Построение поиска в некоторых отношениях очень слабое. Использование синтаксиса тега ("[тема]") очень полезно, и многие люди используют Google ...
dmckee --- котенок бывшего модератора
10

RAII использует семантику деструкторов C ++ для управления ресурсами. Например, рассмотрим умный указатель. У вас есть параметризованный конструктор указателя, который инициализирует этот указатель адресом объекта. Вы размещаете указатель в стеке:

SmartPointer pointer( new ObjectClass() );

Когда интеллектуальный указатель выходит за пределы области видимости, деструктор класса указателя удаляет связанный объект. Указатель выделяется в стеке, а объект - в куче.

Есть определенные случаи, когда RAII не помогает. Например, если вы используете интеллектуальные указатели с подсчетом ссылок (такие как boost :: shared_ptr) и создаете графоподобную структуру с циклом, вы рискуете столкнуться с утечкой памяти, потому что объекты в цикле будут препятствовать освобождению друг друга. Сборка мусора поможет против этого.

Sharptooth
источник
2
Так что это должно называться UCDSTMR :)
Даниэль Даранас
Если подумать, я думаю, что UDSTMR более подходит. Дан язык (C ++), поэтому буква «C» в аббревиатуре не нужна. UDSTMR означает «Использование семантики деструктора для управления ресурсами».
Даниэль Даранас
9

Я хотел бы выразить это немного сильнее, чем предыдущие ответы.

RAII ( Resource Acquisition Is Initialization) означает, что все полученные ресурсы должны быть получены в контексте инициализации объекта. Это запрещает приобретение «голого» ресурса. Причина в том, что очистка в C ++ работает на основе объектов, а не вызовов функций. Следовательно, вся очистка должна выполняться объектами, а не вызовами функций. В этом смысле C ++ более объектно-ориентирован, чем, например, Java. Очистка Java основана на вызовах функций в finallyпредложениях.

MSalters
источник
Отличный ответ. А «инициализация объекта» означает «конструкторы», да?
Чарли Флауэрс,
@ Чарли: да, особенно в этом случае.
MSalters
8

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

Это называется «Приобретение ресурсов - это инициализация», потому что ресурс приобретается, когда создается объект, управляющий ресурсом. Если конструктор не работает (то есть из-за исключения), ресурс не приобретается. Затем, когда объект выходит за пределы области видимости, ресурс освобождается. c ++ гарантирует, что все объекты в стеке, которые были успешно созданы, будут уничтожены (включая конструкторы базовых классов и членов, даже если конструктор суперкласса не работает).

Смысл RAII - сделать исключение получения ресурса безопасным. Все полученные ресурсы правильно высвобождаются независимо от того, где возникает исключение. Однако это действительно зависит от качества класса, который получает ресурс (это должно быть безопасным для исключений, а это сложно).

Iain
источник
Отлично, спасибо, что объяснили причину названия. Насколько я понимаю, вы можете перефразировать RAII следующим образом: «Никогда не приобретайте какие-либо ресурсы через какой-либо другой механизм, кроме (на основе конструктора) инициализации». Да?
Чарли Флауэрс,
Да, это моя политика, однако я очень осторожно отношусь к написанию собственных классов RAII, поскольку они должны быть безопасными в отношении исключений. Когда я их пишу, я стараюсь обеспечить безопасность исключений, повторно используя другие классы RAII, написанные экспертами.
iain 03
Я не нашел их трудным написать. Если ваши классы достаточно маленькие, это совсем не сложно.
Роб К.
7

Проблема со сборкой мусора заключается в том, что вы теряете детерминированное разрушение, которое имеет решающее значение для RAII. Когда переменная выходит за пределы области видимости, сборщик мусора решает, когда объект будет удален. Ресурс, удерживаемый объектом, будет удерживаться до тех пор, пока не будет вызван деструктор.

Марк Рэнсом
источник
4
Проблема не только в детерминизме. Настоящая проблема в том, что финализаторы (именование Java) мешают GC. Сборщик мусора эффективен, потому что он не вызывает мертвые объекты, а, скорее, игнорирует их и забывает. Сборщики мусора должны отслеживать объекты с финализаторами по-другому, чтобы гарантировать, что они вызываются
Дэвид Родригес - dribeas
1
за исключением java / c #, вы, вероятно, очистите блок finally, а не финализатор.
jk.
4

RAII исходит из инициализации выделения ресурсов. По сути, это означает, что когда конструктор завершает выполнение, созданный объект полностью инициализируется и готов к использованию. Это также подразумевает, что деструктор освободит любые ресурсы (например, память, ресурсы ОС), принадлежащие объекту.

По сравнению с языками / технологиями со сборкой мусора (например, Java, .NET), C ++ позволяет полностью контролировать жизнь объекта. Для объекта, выделенного стеком, вы будете знать, когда будет вызван деструктор объекта (когда выполнение выходит за пределы области видимости), что на самом деле не контролируется в случае сборки мусора. Даже используя интеллектуальные указатели в C ++ (например, boost :: shared_ptr), вы будете знать, что при отсутствии ссылки на указанный объект будет вызываться деструктор этого объекта.

Кэтэлин Питиш
источник
3

И как вы можете сделать что-то в стеке, что приведет к очистке того, что живет в куче?

class int_buffer
{
   size_t m_size;
   int *  m_buf;

   public:
   int_buffer( size_t size )
     : m_size( size ), m_buf( 0 )
   {
       if( m_size > 0 )
           m_buf = new int[m_size]; // will throw on failure by default
   }
   ~int_buffer()
   {
       delete[] m_buf;
   }
   /* ...rest of class implementation...*/

};


void foo() 
{
    int_buffer ib(20); // creates a buffer of 20 bytes
    std::cout << ib.size() << std::endl;
} // here the destructor is called automatically even if an exception is thrown and the memory ib held is freed.

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

class mutex
{
   // ...
   take();
   release();

   class mutex::sentry
   {
      mutex & mm;
      public:
      sentry( mutex & m ) : mm(m) 
      {
          mm.take();
      }
      ~sentry()
      {
          mm.release();
      }
   }; // mutex::sentry;
};
mutex m;

int getSomeValue()
{
    mutex::sentry ms( m ); // blocks here until the mutex is taken
    return 0;  
} // the mutex is released in the destructor call here.

Кроме того, есть случаи, когда вы не можете использовать RAII?

Нет, не совсем.

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

Никогда. Сборка мусора решает только очень небольшую часть динамического управления ресурсами.

Роб К
источник
Я очень мало использовал Java и C #, поэтому мне никогда не приходилось его пропускать, но GC определенно ограничивал мой стиль в управлении ресурсами, когда мне приходилось их использовать, потому что я не мог использовать RAII.
Роб К.
1
Я много использовал C # и согласен с вами на 100%. Фактически, я считаю, что недетерминированный сборщик мусора является помехой для языка.
Неманья Трифунович
2

Здесь уже есть много хороших ответов, но я просто хотел бы добавить:
Простое объяснение RAII заключается в том, что в C ++ объект, размещенный в стеке, уничтожается всякий раз, когда он выходит за пределы области видимости. Это означает, что будет вызван деструктор объектов, который сможет выполнить всю необходимую очистку.
Это означает, что если объект создается без «нового», «удалять» не требуется. И это также идея «умных указателей» - они находятся в стеке и по сути являются оболочкой для объекта на основе кучи.

E Dominique
источник
1
Нет, не делают. Но есть ли у вас веская причина когда-либо создавать умный указатель в куче? Кстати, умный указатель был просто примером того, где может быть полезен RAII.
E Dominique
1
Возможно, мое использование «стека» и «кучи» немного небрежно - под объектом в «стеке» я имел в виду любой локальный объект. Естественно, он может быть частью объекта, например, в куче. Под «созданием умного указателя в куче» я имел в виду использовать new / delete для самого умного указателя.
E Dominique
1

RAII - это аббревиатура от Resource Acquisition Is Initialization.

Этот метод очень уникален для C ++ из-за их поддержки как для конструкторов, так и для деструкторов и почти автоматически конструкторов, которые соответствуют этим передаваемым аргументам, или, в худшем случае, конструктор по умолчанию вызывается и деструкторы, если предоставленная явная информация вызывается в противном случае который добавлен компилятором C ++, вызывается, если вы явно не написали деструктор для класса C ++. Это происходит только для объектов C ++, которые управляются автоматически, т. Е. Не используют свободное хранилище (память выделяется / освобождается с помощью операторов new, new [] / delete, delete [] C ++).

Техника RAII использует эту функцию автоматически управляемого объекта для обработки объектов, которые создаются в куче / свободном хранилище, путем явного запроса дополнительной памяти с помощью new / new [], который должен быть явно уничтожен вызовом delete / delete [] . Класс автоматически управляемого объекта обернет этот другой объект, созданный в куче / свободной памяти. Следовательно, когда запускается конструктор автоматически управляемого объекта, обернутый объект создается в куче / свободной памяти, и когда дескриптор автоматически управляемого объекта выходит за пределы области видимости, автоматически вызывается деструктор этого автоматически управляемого объекта, в котором обернутый объект объект уничтожается с помощью удаления. С концепциями ООП, если вы обернете такие объекты внутри другого класса в частной области видимости, у вас не будет доступа к членам и методам обернутых классов и Это причина, по которой разработаны интеллектуальные указатели (также известные как классы дескрипторов). Эти интеллектуальные указатели предоставляют обернутый объект как типизированный объект для внешнего мира и там, позволяя вызывать любые члены / методы, из которых состоит открытый объект памяти. Обратите внимание, что интеллектуальные указатели имеют разные разновидности, основанные на разных потребностях. Вы должны обратиться к Современному программированию на C ++ Андрея Александреску или к реализации / документации библиотеки boost (www.boostorg) shared_ptr.hpp, чтобы узнать больше об этом. Надеюсь, это поможет вам понять RAII. Вы должны обратиться к Современному программированию на C ++ Андрея Александреску или к реализации / документации библиотеки boost (www.boostorg) shared_ptr.hpp, чтобы узнать больше об этом. Надеюсь, это поможет вам понять RAII. Вы должны обратиться к Современному программированию на C ++ Андрея Александреску или к реализации / документации библиотеки boost (www.boostorg) shared_ptr.hpp, чтобы узнать больше об этом. Надеюсь, это поможет вам понять RAII.

Techcraver
источник