Чи потрібна "нарешті" частина "спробувати ... зловити ... нарешті"?


25

Деякі мови (наприклад, C ++ та ранні версії PHP) не підтримують finallyчастину try ... catch ... finallyконструкції. Чи finallyпотрібні коли-небудь? Оскільки код у ньому завжди працює, чому б я / не мав би просто розмістити цей код після try ... catchблоку без finallyзастереження? Навіщо використовувати його? (Я шукаю причину / мотивацію для використання / не використання finally, а не причину, щоб покінчити з «ловом» або чому це робити законно.)


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
maple_shaft

Відповіді:


36

Окрім того, що сказали інші, можливе також виключення виключення всередині застереження. Врахуйте це:

try { 
    throw new SomeException();
} catch {
    DoSomethingWhichUnexpectedlyThrows();
}
Cleanup();

У цьому прикладі Cleanup()функція ніколи не запускається, оскільки виняток потрапляє в застереження про вилов, і наступний найвищий улов у стеку викликів це вловить. Використання остаточного блоку видаляє цей ризик і робить код чистішим для завантаження.


4
Дякую за стислу та пряму відповідь, яка не втягується в теорію, а "мова X краща за територію Y".
Agi Hammerthief

56

Як уже згадували інші, немає гарантії, що код після tryоператора буде виконаний, якщо ви не вкажете всі можливі винятки. Це сказало, це:

try {
   mightThrowSpecificException();
} catch (SpecificException e) {
   handleError();
} finally {
   cleanUp();
}

можна переписати 1 як:

try {
   mightThrowSpecificException();
} catch (SpecificException e) {
   try {
       handleError();
   } catch (Throwable e2) {
       cleanUp();
       throw e2;
   }
} catch (Throwable e) {
   cleanUp();
   throw e;
}
cleanUp();

Але останнє вимагає від вас знайти всі незроблені винятки, дублювати код очищення та не забувати повторно кидати. Так що finallyне потрібно , але це корисно .

C ++ не має, finallyтому що Bjarne Stroustrup вважає, що RAII є кращим або, принаймні, достатньо для більшості випадків:

Чому C ++ не забезпечує "нарешті" конструкцію?

Оскільки C ++ підтримує альтернативу, яка майже завжди є кращою: методика "отримання ресурсів - ініціалізація" (TC ++ PL3, розділ 14.4). Основна ідея - представити ресурс локальним об'єктом, щоб деструктор локального об'єкта звільнив ресурс. Таким чином, програміст не може забути випустити ресурс.


1 Конкретний код для вилучення всіх винятків та повторного скидання без втрати інформації про сліди стека залежить від мови. Я використовував Java, де слід створює стеження, коли створюється виняток. У C # ви просто використали б throw;.


8
Ви також повинні спіймати Винятки у handleError()другому випадку, ні?
Juri Robl

1
Можливо, ви також помиляєтесь. Я б перефразував це catch (Throwable t) {}з блоком спробу .. ловити навколо всього початкового блоку (щоб також ловити кидки handleError)
njzk2

1
Насправді я б додав додатковий спробу лову, який ви пропустили під час дзвінка, handleErro();що зробить ще кращим аргумент щодо того, чому нарешті блоки корисні (навіть якщо це не було первісне питання).
Олексій

1
Ця відповідь насправді не стосується питання, чому C ++ не має finally, що набагато більш нюансовано.
DeadMG

1
@AgiHammerthief вкладеної tryзнаходиться всередині catchдля конкретного винятку. По-друге, можливо, ви не знаєте, чи зможете ви успішно впоратися з помилкою, поки ви не вивчите виняток, або що причина винятку також не дозволяє вам попрацювати з помилкою (принаймні на цьому рівні). Це досить часто при виконанні вводу / виводу. Повторне введення є, тому що єдиний спосіб гарантувати cleanUpпрогони - це захопити все , але оригінальний код дозволить виняткам, що виникають у catch (SpecificException e)блоці, поширюватися вгору.
Довал

22

finally блоки, як правило, використовуються для очищення ресурсів, які можуть допомогти читати при використанні декількох операторів повернення:

int DoSomething() {
    try {
        open_connection();
        return get_result();
    }
    catch {
        return 2;
    }
    finally {
        close_connection();
    }
}

проти

int DoSomething() {
    int result;
    try {
        open_connection();
        result = get_result();
    }
    catch {
        result = 2;
    }
    close_connection();
    return result;
}

2
Я думаю, що це найкраща відповідь. Використання, нарешті, заміни загального винятку просто здається нерозумним. Правильний випадок використання - це очищення ресурсів або аналогічних операцій.
Кік

3
Можливо, навіть більш поширеним є повернення всередину блоку спробу, а не всередині блоку вилову.
Майкл Андерсон

На мій погляд, код недостатньо пояснює використання finally. (Я б використовував код, як і у другому блоці, оскільки декілька заяв про повернення відбиваються там, де я працюю.)
Agi Hammerthief

15

Як ви, мабуть, вже здогадувались, так, C ++ надає ті самі можливості без цього механізму. Таким чином, строго кажучи, try/ finallyмеханізм насправді не потрібен.

Однак сказане, що без цього не висуває певних вимог до способу оформлення іншої мови. У C ++ той самий набір дій втілено в деструкторі класу. Це працює в першу чергу (виключно?), Оскільки виклик деструктора в C ++ є детермінованим. Це, в свою чергу, призводить до деяких досить складних правил щодо життя об'єктів, деякі з яких, очевидно, не інтуїтивні.

Більшість інших мов натомість надають певну форму збору сміття. Хоча про збирання сміття є суперечливі речі (наприклад, його ефективність щодо інших методів управління пам'яттю), одне взагалі не полягає в тому: точний час, коли об'єкт буде "очищений" сміттєзбірником, не прив'язується безпосередньо до обсягу об’єкта. Це запобігає його використанню, коли очищення потрібно детермінізувати або тоді, коли це просто потрібно для правильної роботи, або при роботі з настільки дорогоцінними ресурсами, що їх очищення більшість не затримується довільно. try/ finallyнадає спосіб таким мовам впоратися з тими ситуаціями, які потребують детермінованого очищення.

Я думаю, що ті, хто стверджує, що синтаксис C ++ для цієї можливості є "менш дружелюбним", ніж Java, швидше відсутній. Гірше, що вони пропускають набагато важливіший момент щодо розподілу відповідальності, який виходить далеко за синтаксис і має набагато більше спільного з тим, як розроблений код.

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

Java (і подібні) дещо відрізняються. Хоча вони (на зразок) підтримують finalizeте, що теоретично могло б забезпечити подібні можливості, підтримка настільки слабка, що вона в основному непридатна (і насправді ніколи не використовується).

Як результат, замість того, щоб сам клас міг зробити необхідну очистку, клієнтові класу необхідно вжити заходів для цього. Якщо ми зробимо достатньо короткозорий порівняння, на перший погляд може виявитися, що ця різниця є досить незначною, і Java є досить конкурентоспроможною щодо C ++ у цьому відношенні. Ми закінчуємо щось подібне. У C ++ клас виглядає приблизно так:

class Foo {
    // ...
public:
    void do_whatever() { if (xyz) throw something; }
    ~Foo() { /* handle cleanup */ }
};

... і клієнтський код виглядає приблизно так:

void f() { 
    Foo f;
    f.do_whatever();
    // possibly more code that might throw here
}

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

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

Хоча я називав це "підходом до Java" вище, try/ finallyі подібні механізми під іншими іменами не повністю обмежені лише Java. Для одного видатного прикладу більшість (усіх?) Мов .NET (наприклад, C #) надають те саме.

Останні ітерації як Java, так і C # також забезпечують щось на півдорозі між "класичною" Java та C ++. У C # об’єкт, який хоче автоматизувати очищення, може реалізувати IDisposableінтерфейс, який забезпечує Disposeметод, який (принаймні неясно) схожий на деструктор C ++. Хоча це можна використовувати через try/ finallyяк у Java, C # трохи більше автоматизує завдання за допомогою usingзаяви, яка дозволяє визначати ресурси, які будуть створені як область введення, і знищується при виході області. Хоча це все ще не вистачає рівня автоматизації та визначеності, що надається C ++, це все ще суттєве поліпшення порівняно з Java. Зокрема, класний дизайнер може централізувати деталі того, якрозпоряджатися класом при його реалізації IDisposable. Все, що залишилося для клієнтського програміста, - це менший тягар для написання usingзаяви, щоб переконатися, що IDisposableінтерфейс буде використаний, коли він повинен бути. У Java 7 та новіших версіях назви були змінені для захисту винних, але основна ідея в основному однакова.


1
Ідеальна відповідь. Деструктори - НАЙКРАЩА функція в C ++.
Томас Едінг

13

Не можете повірити, що ніхто інший цього не піднімав (жоден каламбур) - ви цього не робите потрібно в зловити положення!

Це цілком розумно:

try 
{
   AcquireManyResources(); 
   DoSomethingThatMightFail(); 
}
finally 
{
   CleanUpThoseResources(); 
}

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

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


9

Що буде, якби було викинуто виняток, якого ви не очікували. Спробу буде вийти посередині, і жодне застереження не виконується.

Нарешті, блоком є ​​допомога в цьому та переконання, що незалежно від того, чисте очищення не відбудеться.


4
Це не є достатньою причиною для finally, тому що ви можете запобігти "несподіваним" виняткам з catch(Object)або catch(...)загалом.
MSalters

1
Це звучить як робота навколо. Концептуально нарешті чистіше. Хоча я мушу зізнатися, що рідко його використовую.
швидко_значення

7

Деякі мови пропонують як конструктори, так і деструктори для своїх об'єктів (наприклад, C ++, я вірю). За допомогою цих мов ви можете виконати більшість (можливо, все) того, що зазвичай робиться в finallyдеструкторі. Як такий - на цих мовах - finallyпункт може бути зайвим.

Мовою без деструкторів (наприклад, Java) складно (можливо, навіть неможливо) досягти правильної очистки без finallyзастереження. Примітка. У Java існує finaliseметод, але немає гарантії, що він коли-небудь буде викликаний.


Може бути корисним зазначити, що деструктори допомагають очищати ресурси, коли руйнування детерміновано . Якщо ми не знаємо, коли об’єкт буде знищений та / або зібраний сміття, то деструктори недостатньо безпечні.
Морвен

@Morwenn - Гарний момент. Я натякнув на це своїм посиланням на Java, finaliseале в даний час я вважаю за краще не вникати в політичні аргументи навколо деструкторів / фіналів.
OldCurmudgeon

У З ++ руйнування детерміновано. Коли область, що містить автоматичний об'єкт, виходить (наприклад, вона вискакує зі стека), викликається її деструктор. (C ++ дозволяє виділяти об'єкти на стеку, а не тільки на купі.)
Роб K

@RobK - І це точна функціональність, finaliseале з розтяжним смаком і механізмом, схожим на ооп - дуже виразний і порівнянний з finaliseмеханізмом інших мов.
OldCurmudgeon

1

Нарешті спробуйте і спробуйте зловити дві різні речі, які розділяють лише ключове слово: "спробувати". Особисто мені хотілося б бачити таке інше. Причина, коли ви їх бачите разом, полягає в тому, що винятки створюють "стрибок".

І спроба нарешті призначена для запуску коду, навіть якщо потік програмування вискакує. Будь то через виняток чи будь-яку іншу причину. Це акуратний спосіб придбати ресурс і переконатися, що після його очищення не потрібно турбуватися про стрибки.


3
У .NET вони реалізуються за допомогою окремих механізмів; Однак у Java єдина конструкція, визнана JVM, семантично еквівалентна "on goto error", шаблон, який безпосередньо підтримує, try catchале ні try finally; код за допомогою останнього перетворюється в код, використовуючи лише перший, шляхом копіювання вмісту finallyблоку на всі місця коду, де це може знадобитися для виконання.
supercat

@supercat приємно, дякую за додаткову інформацію про Java.
Пітер Б

1

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

Причини, за якими ви можете використовувати остаточний блок, а не код після блоку спробу

  • ви повертаєтесь із блоку спробу: врахуйте це

    Database db = null;
    try {
     db = open_database();
     if(db.isSomething()) {
       return 7;
     }
     return db.someThingElse();
    } finally {
      if(db!=null)
        db.close();
    }
    

    в порівнянні з:

    Database db = null;
    int returnValue = 0;
    try {
     db = open_database();
     if(db.isSomething()) {
       returnValue = 7;
     } else {
       returnValue = db.someThingElse();
     }
    } catch(Exception e) {
      if(db!=null)
        db.close();
    }
    return returnValue;
    
  • Ви повертаєтесь рано з блоку (ів) лову: Порівняйте

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      return 7;
    } catch (DBIsADonkeyException e ) {
      return 11;
    } finally {
      if(db!=null)
        db.close();
    }
    

    vs:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      if(db!=null) 
        db.close();
      return 7;
    } catch (DBIsADonkeyException e ) {
      if(db!=null)
        db.close();
      return 11;
    }           
    db.close();
    
  • Ви переосмислюєте винятки. Порівняйте:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      throw convertToRuntimeException(e,"DB was wonkey");
    } finally {
      if(db!=null)
        db.close();
    }
    

    vs:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      if(db!=null)
        db.close();
      throw convertToRuntimeException(e,"DB was wonkey");
    } 
    if(db!=null)
      db.close();
    

Ці приклади не здаються занадто поганими, але часто у вас є кілька таких випадків взаємодії та більше, ніж один виняток / тип ресурсу в грі. finallyможе допомогти вам не перетворити ваш код у заплутаний кошмар з обслуговування.

Тепер у C ++ з цими об'єктами можна обробляти об'єкти. Але в ІМО існує два недоліки цього підходу 1. Синтаксис менш дружній. 2. Порядок будівництва, який є зворотним порядком руйнування, може зробити речі менш зрозумілими.

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


1

Все, що логічно "необхідно" мові програмування, - це інструкції:

assignment a = b
subtract a from b
goto label
test a = 0
if true goto label

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

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


1
Ваша відповідь, безумовно, правдива, але я не кодую в зборах; це занадто боляче Я запитую, чому використовувати функцію, в якій я не бачу сенсу в мовах, які її підтримують, а не в тому, що це мінімальний набір інструкцій для мови.
Agi Hammerthief

1
Справа в тому, що будь-яка мова, що реалізує саме ці 5 операцій, може реалізувати будь-який алгоритм - хоч і досить покрутливо. Більшість версій / операторів на мовах високого рівня не є "необхідними", якщо метою є лише впровадження алгоритму. Якщо метою є швидкий розвиток читабельного коду, який можна читати, то більшість з них є необхідними, але "читабельні" та "реконструйовані" не піддаються вимірюванню та надзвичайно суб'єктивні. Приємні розробники мови містять багато можливостей: якщо ви не користуєтесь деякими з них, тоді не користуйтеся ними.
Джеймс Андерсон

0

Насправді більший розрив для мене зазвичай є в мовах, які підтримують, finallyале деструкторів не вистачає, тому що ви можете змоделювати всю логіку, пов'язану з "очищенням" (який я розділю на дві категорії) через деструктори на центральному рівні, не маючи вручну роботи з очищенням логіка в кожній відповідній функції. Коли я бачу, що C # або Java-коди роблять такі речі, як розблокування файлів вручну та закриття файлів у finallyблоках, це відчуває себе застарілим і схожим на код C, коли все це автоматизовано в C ++ через деструктори таким чином, що звільняє людину від відповідальності.

Однак я все одно знайду легку зручність, якщо включено C ++, finallyі це тому, що є два види очищення:

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

Другий, принаймні, не настільки інтуїтивно відповідає ідеї знищення ресурсів, хоча ви можете це зробити просто чудово за допомогою охоронців сфери, які автоматично відкочують зміни, коли вони знищуються до того, як вони будуть скоєні. Там, finallyнапевно, передбачено принаймні трохи (лише підлітковий біт) більш простий механізм роботи, ніж охоронці сфери.

Однак ще більш простим механізмом буде rollbackблок, якого я ніколи не бачив жодної мови. Це моя мрія, якщо я коли-небудь розробляв мову, яка передбачала обробку виключень. Це буде нагадувати таке:

try
{
    // Cause external side effects. These side effects should
    // be undone if we don't finish successfully.
}
rollback
{
    // Reverse external side effects. This block is *only* executed 
    // if the 'try' block above faced a premature return out 
    // of the function. It is different from 'finally' which 
    // gets executed regardless of whether or not the function 
    // exited prematurely. This block *only* gets executed if we 
    // exited prematurely from  the try block so that we can undo 
    // whatever side effects it failed to finish making. If the try 
    // block succeeded and didn't face a premature exit, then we 
    // don't want this block to execute.
}

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

Також є деякі незрозумілі випадки, коли ви просто хочете робити різні речі, незалежно від того, що після виходу з функції, незалежно від того, як вона вийшла, як, наприклад, реєстрація часової позначки. Можливо, finallyє найпростішим та ідеальним рішенням для роботи, оскільки спроба призначати об'єкт лише для використання його деструктора з єдиною метою реєстрації часової позначки просто виглядає дивно (хоча ви можете робити це просто чудово та досить зручно з лямбдами ).


-9

Як і у багатьох інших незвичайних речей щодо мови C ++, відсутність try/finallyконструкції є вадою дизайну, якщо ви навіть можете назвати це тим, що на мові, яка, здається, взагалі не була виконана жодна реальна проектна робота .

RAII (використання виклику детермінованого деструктора на очищенні об'єктів для очищення) має два серйозні недоліки. Перший полягає в тому, що воно вимагає використання об'єктів на основі стека , які є мерзотою, що порушує Принцип заміщення Ліскова. Є безліч вагомих причин, чому жодна інша мова ОО до того часу, як C ++ їх не використовувала - в межах epsilon; D не рахується, оскільки він базується на C ++ і не має частки ринку - і пояснення проблем, які вони викликають, виходить за рамки цієї відповіді.

По-друге, те, що finallyможна зробити, - це набір знищення об'єктів. Багато з того, що робиться з RAII на C ++, було б описано мовою Delphi, яка не має сміття, із наступним малюнком:

myObject := MyClass.Create(arguments);
try
   doSomething(myObject);
finally
   myObject.Free();
end;

Це шаблон RAII, зроблений явним; якби ви створили процедуру C ++, яка містить лише еквівалент першому та третьому рядку вище, те, що створив би компілятор, виглядав би так, як я написав у його базовій структурі. А оскільки це єдиний доступ до try/finallyконструкції, яку надає C ++, розробники C ++ в кінцевому підсумку мають досить міопічне уявлення try/finally: коли все, що у вас є, є молотком, все починає виглядати як руйнівник, так би мовити.

Але є й інші речі, які досвідчений розробник може зробити з finallyконструкцією. Йдеться не про детерміновані знищення, навіть за умови винятку винятку; мова йде про детерміновані виконання коду , навіть за умови виникнення винятку.

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

dataset.DisableControls();
try
   LoadData(dataset);
finally
   dataset.EnableControls();
end;

Зрозуміло, що тут не знищується жоден об'єкт, і немає потреби в ньому. Код простий, стислий, явний та ефективний.

Як це зробити в C ++? Ну, спочатку вам доведеться кодувати весь клас . Це, мабуть, DatasetEnablerназветься чи якось таке. Все його існування було б помічником RAII. Тоді вам потрібно буде зробити щось подібне:

dataset.DisableControls();
{
   raiiGuard = DatasetEnabler(dataset);
   LoadData(dataset);
}

Так, ці, очевидно, зайві фігурні фігурні дужки необхідні для управління належним масштабуванням та забезпечення того, що набір даних буде знову ввімкнено негайно, а не в кінці методу. Таким чином, те, що ви закінчуєте, не займає менше рядків коду (якщо ви не використовуєте єгипетські дужки). Для цього потрібно створити зайвий об’єкт, який має над головою. (Чи не повинен код C ++ бути швидким?) Це не явно, але натомість покладається на магію компілятора. Код, який виконується, в цьому методі ніде не описаний, але замість цього знаходиться у зовсім іншому класі, можливо, у зовсім іншому файлі . Коротше кажучи, це жодним чином не краще рішення, ніж можливість самостійно написати try/finallyблок.

Така проблема досить поширена в мовному дизайні, що існує її назва: інверсія абстракції. Це відбувається, коли конструкція високого рівня побудована поверх конструкції низького рівня, і тоді конструкція низького рівня не підтримується безпосередньо мовою, що вимагає від тих, хто бажає використовувати її для повторної реалізації. конструкція високого рівня, часто суворо карається як читабельність коду, так і ефективність.


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