Розуміння значення терміна та поняття - RAII (Придбання ресурсів - ініціалізація)


110

Чи можете ви розробити розробники C ++, будь ласка, добре опишіть, що таке RAII, чому він важливий і чи може він мати якесь відношення до інших мов?

Я зробити трохи знаю. Я вважаю, що це означає "Придбання ресурсів - це ініціалізація". Однак це ім'я не збігається з моїм (можливо, неправильним) розумінням того, що таке RAII: у мене складається враження, що RAII - це спосіб ініціалізації об'єктів на стеку таким чином, що, коли ці змінні вийдуть за межі, деструктори автоматично викликати спричинення ресурсів для очищення.

То чому б це не називалося "використанням стека для запуску очищення" (UTSTTC :)? Як дістатися звідти до "RAII"?

І як можна зробити щось на стеку, що спричинить очищення чогось, що живе на купі? Також бувають випадки, коли ви не можете використовувати RAII? Ви коли-небудь виявляєте бажання збору сміття? Принаймні збирач сміття, який ви могли використовувати для одних об’єктів, дозволяючи іншим керувати?

Дякую.


27
UTSTTC? Мені це подобається! Це набагато інтуїтивніше, ніж RAII. RAII це погано по імені, я сумніваюся , що C ++ програміст буде оскаржувати це. Але це не просто змінити. ;)
jalf

10
Ось погляд Stroustrup на цю тему: groups.google.com/group/comp.lang.c++.moderated/msg/…
sbi

3
@sbi: У будь-якому випадку, +1 на ваш коментар лише для історичного дослідження. Я вважаю, що точка зору автора (B. Stroustrup) на ім'я концепції (RAII) досить цікава, щоб мати власну відповідь.
paercebal

1
@paercebal: Історичні дослідження? Тепер ти змусив мене почуватися дуже старим. :(Тоді я читав цілу нитку і навіть не вважав себе новичкою на C ++!
sbi

3
+1, я збирався задати те саме питання, радий, що я не єдиний, хто розуміє поняття, але не має сенсу цього імені. Здається, це повинно було називатися RAOI - Придбання ресурсів при ініціалізації.
Лоран

Відповіді:


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"));
}

Обробляються три випадки помилок: жоден файл не можна відкрити, лише один файл можна відкрити, обидва файли можна відкрити, але копіювання файлів не вдалося. У процесі реалізації, Fooяка не належить до RAII, усі три випадки повинні були б чітко розглянути.

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деструктор 's, щоб випустити файли. Але якщо він originalбув побудований, його деструктор буде викликаний під час очищення Loggerконструктора.

RAII спрощує очищення після часткової побудови.


Негативні моменти:

Негативні моменти? Усі проблеми можна вирішити за допомогою RAII та смарт-покажчиків ;-)

RAII іноді є непростим, коли вам потрібно затримати придбання, штовхаючи агреговані предмети на купу.
Уявіть собі Logger потрібен SetTargetFile(const char* target). У цьому випадку рукоятка, яка все ще має бути членом Logger, повинна знаходитися на купі (наприклад, у розумному покажчику, щоб запустити руйнування ручки належним чином.)

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

У мене була одна особливо складна структура, яка могла б отримати користь від GC, де "прості" розумні покажчики викликали б кругові посилання на кілька класів. Ми заплуталися через ретельне врівноваження сильних і слабких покажчиків, але щоразу, коли ми хочемо щось змінити, ми повинні вивчити велику діаграму відносин. GC може бути кращим, але деякі компоненти містять ресурси, які повинні бути випущені якнайшвидше.


Примітка до зразка FileHandle: він не мав бути повним, а лише зразок - але виявився невірним. Дякую Йоханнесу Шаубу за вказівку та FredOverflow за перетворення його на правильне рішення C ++ 0x. З часом я погодився з документованим тут підходом .


1
+1 Для вказівки на те, що GC та ASAP не пов'язані між собою. Не боляче часто, але коли це робиться, діагностувати нелегко: /
Матьє М.

10
Зокрема, одне речення, яке я не помітив у попередніх прочитах. Ви сказали, що "RAII" говорить вам: "Придбайте свої ресурси всередині конструкторів". Це має сенс і є майже паралельною фразою слова "RAII". Тепер я розумію це ще краще (я б голосував за вас ще раз, якщо зможу :)
Charlie Flowers

2
Однією з головних переваг GC є те, що рамка розподілу пам’яті може запобігти створенню звисаючих посилань за відсутності «небезпечного» коду (якщо дозволений «небезпечний» код, звичайно, рамки нічого не можуть перешкодити). GC також часто перевершує RAII при роботі з спільними незмінними об'єктами, такими як рядки, які часто не мають чіткого власника і не потребують очищення. Прикро, що більше фреймворків не прагнуть поєднувати GC і RAII, оскільки в більшості додатків буде поєднання незмінних об'єктів (де GC найкраще) та об'єктів, які потребують очищення (де RAII найкраще).
supercat

@supercat: Я зазвичай люблю GC - але він працює лише для ресурсів, які GC "розуміє". Наприклад, .NET GC не знає вартість об'єктів COM. Якщо просто створити та знищити їх у циклі, він із задоволенням дозволить програмі врізатись у землю щодо адресного простору чи віртуальної пам’яті - що б там не було спочатку - навіть не замислюючись про те, чи можливо робити GC. --- окрім того, навіть у ідеальному середовищі GC'd я все-таки пропускаю силу детермінованих руйнувань: ви можете застосувати ту саму схему до інших артефактів, наприклад, показуючи елементи інтерфейсу в сертифікаційних умовах.
peterchen

@peterchen: Я вважаю, що у багатьох OOP-мисленнях відсутня концепція власності на об'єкти. Відстеження права власності часто очевидно необхідне для об'єктів з ресурсами, але також часто необхідно для об'єктів, що змінюються, без ресурсів. Загалом, об'єкти повинні інкапсулювати свій змінений стан або у посиланнях на об'єкти, що змінюються, що поділяються, або в об'єктах, що змінюються, які є виключним власником. Така ексклюзивна власність не обов'язково передбачає ексклюзивний доступ до запису, але якщо вона Fooє власником Barі Bozвимкнює її, ...
supercat

42

Там є чудові відповіді, тому я просто додаю забуті речі.

0. RAII - це сфери дії

RAII стосується обох:

  1. придбання ресурсу (незалежно від того, який ресурс) у конструктора, і недоотримання його в деструкторі.
  2. виконання конструктора, коли змінна оголошена, і деструктор автоматично виконується, коли змінна виходить із сфери застосування.

Інші вже відповіли на це, тому я не буду деталізувати.

1. Під час кодування в Java або C # ви вже використовуєте RAII ...

МОНІСЬКА РОБОТА: Що! Коли я кажу: "Ніколь, принеси мені тапочки і дай мені нічний капелюшок", це проза?

МАЙСТЕР ФІЛОСОФІЇ: Так, сер.

ПІСНЯ МОНСЮР: Я вже більше сорока років розмовляю з прозою, нічого про це не знаючи, і я вам дуже зобов'язаний за те, що ви навчили мене цього.

- Мольєр: Джентльмен середнього класу, акт 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: Придбання mutex виконується за ключовим словом ( 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 ++ не має остаточного підрозділу. І це не помилка, оскільки C ++ її не потребує, оскільки RAII вже обробляє ресурси для видалення ресурсів. (І повірте, писати деструктор C ++ набагато простіше, ніж писати правильний пункт Java, нарешті, або навіть правильний метод розпорядження C #).

І все-таки іноді finallyзастереження було б крутим. Чи можемо ми це зробити на C ++? Так, ми можемо! І знову з почерговим використанням RAII.

Висновок: RAII - це більше, ніж філософія в C ++: це C ++

РАЙ? ЦЕ С ++ !!!

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

Досягнувши певного рівня досвіду роботи на C ++, ви починаєте думати з точки зору RAII , з точки зору автоматизованого виконання конструкторів і деструкторів .

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

І майже все підходить з точки зору RAII: безпека винятків, мьютекси, підключення до бази даних, запити до бази даних, підключення до сервера, годинник, ручки ОС тощо, і останнє, але не менш важливе значення - пам'ять.

Частина бази даних не є незначною, оскільки, якщо ви погоджуєтесь платити ціну, ви навіть можете писати в стилі " транзакційного програмування ", виконуючи рядки та рядки коду до прийняття рішення, врешті-решт, якщо ви хочете здійснити всі зміни або, якщо це неможливо, повернення всіх змін назад (до тих пір, поки кожен рядок задовольняє принаймні гарантії сильного винятку). (див. другу частину цієї статті Герба Саттера про програмування транзакцій).

І як головоломка, все підходить.

RAII - це стільки частина C ++, C ++ не може бути C ++ без нього.

Це пояснює, чому досвідчені розробники C ++ так захоплені RAII, і чому RAII - це перше, що вони шукають, намагаючись іншу мову.

І це пояснює, чому сміттєзбірник, хоча сам по собі є найбільш важливим елементом технології, не так вражає з точки зору розробника C ++:

  • RAII вже обробляє більшість випадків, якими займається GC
  • GC краще, ніж RAII, має кругові посилання на чисто керовані об'єкти (пом'якшені розумним використанням слабких покажчиків)
  • Але GC обмежений пам'яттю, тоді як RAII може обробляти будь-який ресурс.
  • Як описано вище, RAII може зробити набагато, набагато більше ...

Шанувальник Java: Я б сказав, що GC набагато корисніше RAII, оскільки він обробляє всю пам'ять і звільняє вас від багатьох потенційних помилок. За допомогою GC ви можете створювати кругові посилання, повертати та зберігати посилання, і їх важко зрозуміти неправильно (зберігання посилання на нібито короткочасний об’єкт подовжує час його живлення, що є свого роду витоком пам'яті, але це єдина проблема) . Поводження з ресурсами за допомогою GC не працює, але більшість ресурсів у додатку мають тривіальний живий цикл, а кілька інших, що залишилися, не мають нічого особливого. Я б хотів, щоб ми могли мати і GC, і RAII, але це здається неможливим.
maaartinus

16

1
Деякі з них відповідають правим моїм запитанням, але пошук не викликав їх, а також список "пов'язаних питань", який з’являється після введення нового питання. Дякуємо за посилання.
Charlie Flowers

1
@Charlie: Набір пошуків дуже слабкий. Використання синтаксису тегів ("[тема]") дуже корисне, і багато людей використовують Google ...
dmckee --- кошеня колишнього модератора

10

RAII використовує семантику деструкторів C ++ для управління ресурсами. Наприклад, розгляньте розумний вказівник. У вас є параметризований конструктор вказівника, який ініціалізує цей покажчик з адресою об'єкта. Ви виділяєте вказівник на стек:

SmartPointer pointer( new ObjectClass() );

Коли розумний покажчик виходить за межі, деструктор класу вказівника видаляє підключений об'єкт. Вказівник виділяється стеком, а об'єкт - купово-виділеним.

Є певні випадки, коли RAII не допомагає. Наприклад, якщо ви використовуєте розумні покажчики підрахунку посилань (наприклад, boost :: shared_ptr) і створюєте структуру, схожу на графік, з циклом, ви ризикуєте зіткнутися з витоком пам’яті, оскільки об’єкти в циклі не дозволять один одному звільнятися. Збирання сміття допомогло б проти цього.


2
Тож його слід назвати UCDSTMR :)
Даніель Даранас

По-друге, я думаю, що UDSTMR є більш підходящим. Мова (C ++) наведена, тому літера "C" не потрібна в абревіатурі. UDSTMR розшифровується як Використання семантики деструктора для управління ресурсами.
Даніель Даранас

9

Я хотів би сказати це трохи сильніше, ніж попередні відповіді.

RAII, Придбання ресурсів - це ініціалізація означає, що всі придбані ресурси повинні бути придбані в контексті ініціалізації об'єкта. Це забороняє «оголене» придбання ресурсів. Обґрунтування полягає в тому, що очищення в C ++ працює на об'єктній основі, а не на основі функціональних викликів. Отже, все очищення повинно здійснюватися об'єктами, а не функціональними викликами. У цьому сенсі С ++ орієнтований більше на об'єкти, ніж, наприклад, Java. Очищення Java ґрунтується на викликах функцій у finallyпунктах.


Чудова відповідь. А "ініціалізація об'єкта" означає "констриктори", так?
Charlie Flowers

@Charlie: так, особливо в цьому випадку.
MSalters

8

Я погоджуюся з ципітом. Але хотілося б додати, що ресурсами може бути що завгодно, а не лише пам’ять. Ресурсом може бути файл, критичний розділ, потік або з'єднання з базою даних.

Це називається "Придбання ресурсів" - це ініціалізація, оскільки ресурс набувається тоді, коли об'єкт, що контролює ресурс, побудований, Якщо конструктор вийшов з ладу (тобто через виняток), ресурс не придбаний. Потім, як тільки об'єкт виходить з рамки, ресурс вивільняється. c ++ гарантує, що всі об'єкти в стеку, які були успішно сконструйовані, будуть знищені (сюди входять конструктори базових класів та члени, навіть якщо конструктор суперкласу виходить з ладу).

Раціональне значення RAII полягає в тому, щоб зробити виняток із придбання ресурсів безпечним. Щоб усі придбані ресурси були належним чином звільнені, незалежно від того, де відбувається виняток. Однак це покладається на якість класу, який набуває ресурс (це повинно бути виключно безпечним, і це важко).


Відмінно, дякую за пояснення обґрунтування назви. Як я розумію, ви можете перефразовувати RAII так: "Ніколи не купуйте будь-який ресурс за допомогою будь-якого іншого механізму, ніж (на основі конструктора) ініціалізації". Так?
Charlie Flowers

Так, це моя політика, проте я дуже насторожено пишу свої власні класи RAII, оскільки вони повинні бути безпечними для винятків. Коли я їх пишу, я намагаюся забезпечити безпеку виключень, використовуючи інші класи RAII, написані експертами.
Iain

Мені не складно писати. Якщо ваші заняття належним чином недостатньо, вони зовсім не важкі.
Роб К

7

Проблема з вивезенням сміття полягає в тому, що ви втрачаєте детерміновані руйнування, які мають вирішальне значення для RAII. Після того, як змінна вийде за межі сфери, це залежить від збору сміття, коли об’єкт буде відшкодований. Ресурс, який зберігається об'єктом, буде зберігатися до виклику деструктора.


4
Проблема полягає не лише у детермінізмі. Справжня проблема полягає в тому, що фіналізатори (називання Java) заважають GC. ГК ефективний, оскільки не згадує мертві предмети, а швидше ігнорує їх у небуття. ГК повинні відслідковувати об'єкти з фіналізаторами по-іншому, щоб гарантувати їх виклик
Девід Родрігес - дрибей

1
за винятком java / c # ви б, ймовірно, прибирали в остаточному блоці, а не в фіналізаторі.
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%. Насправді я вважаю недетермінований GC відповідальністю у мові.
Неманья Трифунович

2

Тут вже є багато хороших відповідей, але я хотів би лише додати:
Просте пояснення RAII полягає в тому, що в C ++ об'єкт, виділений на стек, знищується кожного разу, коли він виходить за межі області. Це означає, що деструктор об’єктів буде викликаний і може зробити все необхідне очищення.
Це означає, що якщо об’єкт створений без "нового", "видалення" не потрібно. І це також ідея "розумних покажчиків" - вони знаходяться на стеці і фактично обмотують об'єкт на основі купи.


1
Ні, вони ні. Але у вас є вагомі підстави коли-небудь створити розумний покажчик на купі? До речі, розумний покажчик був лише прикладом того, де RAII може бути корисним.
E Dominique

1
Можливо, моє використання «стека» проти «купи» трохи неохайне - під об’єктом на «стеці» я мав на увазі будь-який локальний об’єкт. Це, природно, може бути частиною предмета, наприклад, на купі. Під "створенням розумного вказівника на купу" я мав на увазі використовувати новий / видалити на самому смарт-покажчику.
E Dominique

1

RAII - абревіатура для ініціалізації придбання ресурсів.

Ця методика дуже унікальна для C ++ через те, що вони підтримують як Конструктори, так і Деструктори, і майже автоматично конструктори, які відповідають тим, що передаються аргументи, або в гіршому випадку конструктор за замовчуванням називається & destructors, якщо надана експліцитність називається інакше що додається компілятором C ++, викликається, якщо ви явно не написали деструктор для класу C ++. Це трапляється лише для об'єктів C ++, якими керує авто - це означає, що вони не використовують вільний магазин (пам'ять, виділена / розміщена з використанням нових, нових [] / видалення, видалення [] операторів C ++).

Техніка RAII використовує цю функцію керованого автоматично керованого об’єкта для обробки об'єктів, створених у купі / вільному магазині, explcitly з проханням отримати більше пам’яті за допомогою new / new [], який слід явно знищити за допомогою виклику delete / delete [] . Клас об'єкта, що керується автоматично, оберне цей інший об'єкт, створений на пам'яті купи / вільного зберігання. Отже, коли запускається конструктор автоматично керованого об'єкта, загортається об'єкт створюється на купі / вільній пам'яті, і коли ручка об'єкта автоматично керується, виходить із сфери дії, деструктор цього автоматично керованого об'єкта викликається автоматично, в якому загортається об'єкт знищується за допомогою видалення. З концепціями OOP, якщо ви загортаєте подібні об'єкти в інший клас в приватному масштабі, ви не мали б доступу до членів загорнутого класу та методів & саме тому розроблені розумні покажчики (ака-класи обробки). Ці розумні покажчики піддають обернутий об'єкт як введений об'єкт у зовнішній світ та там, дозволяючи викликати будь-яких членів / методів, з яких складається об'єкт пам'яті. Зауважте, що розумні покажчики мають різні аромати, засновані на різних потребах. Щоб дізнатись більше про це, зверніться до сучасного програмування на C ++ від Андрія Олександреску або до впровадження / документації збільшеної бібліотеки (www.boostorg) shared_ptr.hpp. Сподіваюся, це допоможе вам зрозуміти RAII. Щоб дізнатись більше про це, зверніться до сучасного програмування на C ++ від Андрія Олександреску або до впровадження / документації збільшеної бібліотеки (www.boostorg) shared_ptr.hpp. Сподіваюся, це допоможе вам зрозуміти RAII. Щоб дізнатись більше про це, зверніться до сучасного програмування на C ++ від Андрія Олександреску або до впровадження / документації збільшеної бібліотеки (www.boostorg) shared_ptr.hpp. Сподіваюся, це допоможе вам зрозуміти RAII.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.