Чи недетермінований менеджмент ресурсів є вичерпною абстракцією?


9

Як я бачу, існують дві поширені форми управління ресурсами: детерміновані знищення та явні. Прикладом першого можуть бути деструктори C ++ та розумні покажчики або підрозділ DESTROY Perl, тоді як прикладом останнього може бути парадигма блоків для управління ресурсами Ruby або інтерфейс ID.

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

Моє запитання таке: враховуючи, що деструктори для розумних покажчиків або системи збору сміття - майже однакове - дозволяють неявне та прозоре знищення ресурсів, чи це менш витікаюча абстракція, ніж недетерміновані типи, які покладаються на явні позначення?

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

В іншому прикладі використовуються блоки Ruby. Дві підкласи потребують звільнення ресурсів, тому надклас вибирає інтерфейс, який використовує блок у конструкторі, навіть якщо інші конкретні підкласи можуть не потребувати цього, оскільки вони не потребують спеціального знищення.

Чи трапляється так, що останні просочуються деталями впровадження знищення ресурсів, а перші - не?

EDIT: Порівнювати, скажімо, Ruby з Perl може бути справедливішим, оскільки один має детерміновані руйнування, а другий - ні, але вони обидва зібрані сміття.


5
Мені спокуса сказати "так", але мені подобається чути, що з цього приводу мають сказати інші.
Барт ван Іґен Шенау

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

@Giorgio: "Шляхи доступу до об'єкта" досить розпливчасті. Ви маєте на увазі читати чи писати? Конст / нестабільна кваліфікація? Покажчики насправді не є "способом доступу до об'єкта"; майже будь-який вираз призводить до об'єкта, і перенаправлення покажчика просто не так особливе.
MSalters

1
@Giorgio: У цьому сенсі OOP не можна надсилати повідомлення на покажчик C ++. Щоб послати повідомлення (*ptr).Message()або рівнозначно, вам потрібно знецінити вказівник ptr->Message(). Існує нескінченний набір дозволених виразів, як ((*ptr))->Messageце також еквівалент. Але всі вони зводяться доexpressionIdentifyingAnObject.Message()
MSalters

1
Скидаючи гроші, потрібно бути обережними щодо того, щоб уникати кіл. Тож ця абстракція також протікає, просто по-іншому.
CodesInChaos

Відповіді:


2

Ваш власний приклад відповідає на питання. Прозоре руйнування явно менш герметичне, ніж явне знищення. Він може протікати, але менш протікає.

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

Деякі переваги прозорого знищення над явним: -
однакова схема використання -
ви не можете забути випустити ресурс.
--чисті деталі не засмічують пейзаж у місці використання.


2

Невдача в абстракції насправді полягає не в тому, що збирання сміття не є детермінованим, а скоріше в ідеї, що об’єкти "зацікавлені" у речах, на які вони посилаються, і не цікавляться речами, до яких вони не затримуються. посилання. Щоб зрозуміти чому, розглянемо сценарій об’єкта, який підтримує лічильник того, як часто малюється певний елемент управління. Під час створення він передплачує подію "фарби" управління, а при видаленні відміняє підписку. Подія клацання просто збільшує поле, і метод getTotalClicks()повертає значення цього поля.

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

Якщо, наприклад, методом було створити новий об'єкт "лічильник фарби", виконати якусь дію над елементом управління, спостерігати, скільки разів керування перефарбовували, а потім відмовлятися від об'єкта лічильника фарб, об'єкт залишатиметься підписаним на подію навіть хоча ніхто ніколи не піклується про те, щоб об’єкт та всі посилання на нього просто зникли. Об'єкти, однак, не отримали право на збір, доки сам контроль не відбудеться. Якби метод був таким, до якого протягом життя елемента керували б багато тисяч разів [правдоподібний сценарій], це може спричинити переповнення пам’яті, але для того, що вартість N викликів, ймовірно, буде O (N ^ 2) або O (N ^ 3), якщо обробка підписки не була дуже ефективною і більшість операцій насправді не передбачала жодного фарбування.

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

Якщо жодної посилання на об'єкт не існує ніде у Всесвіті, об'єкт може бути безпечно вважатися марним та усунутий від існування. Факт існування посилання на об’єкт, однак, не означає, що об’єкт є "корисним". У багатьох випадках фактична корисність об'єктів буде залежати від наявності посилань на інші об'єкти, які - з точки зору GC - абсолютно не пов'язані з ними.

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


0

Ніякий "деструктор або інший інтерфейс, який говорить" цей клас повинен бути знищений ", не є договором цього інтерфейсу. Якщо ви робите підтип, який не потребує спеціального знищення, я схиляюся до того, що це порушення принципу заміни Ліскова. .

Що стосується C ++ порівняно з іншими, то тут немає великої різниці. C ++ змушує інтерфейс усіх об'єктів. Абстракції не можуть просочуватися, коли цього вимагає мова.


4
"Якщо ви робите підтип, який не потребує спеціального знищення" Це не є порушенням LSP, оскільки no-op є дійсним спеціальним випадком знищення. Проблема полягає в тому, коли ви додаєте вимогу знищення до похідного класу.
CodesInChaos

Я тут плутаюсь. Якщо потрібно додати спеціальний код знищення до підкласу C ++, він зовсім не змінює його схеми використання, оскільки це автоматично. Це означає, що надклас та підклас все ще можуть бути взаємозамінними. Але при чіткому позначенні управління ресурсами, підклас, який потребує явного знищення, зробить його використання несумісним із надкласом, чи не так? (Припускаючи, що суперклас не потребує явного знищення.)
Луї Джекман

@CodesInChaos - так, я думаю, це правда.
Теластин

@ljackman: Клас, який потребує спеціального знищення, накладає тягар тому, хто викликає його конструктор, щоб забезпечити його виконання. Це не створює порушення LSP, оскільки DerivedFooThatRequiresSpecialDestructionможна створити лише код, який викликає new DerivedFooThatRequiresSpecialDestruction(). З іншого боку, заводський метод, який повернув DerivedFooThatRequiresSpecialDestructionкод, який не очікував чогось, що вимагає знищення, буде порушенням LSP.
supercat

0

Моє запитання таке: враховуючи, що деструктори для розумних покажчиків або системи збору сміття - майже однакове - дозволяють неявне та прозоре знищення ресурсів, чи це менш витікаюча абстракція, ніж недетерміновані типи, які покладаються на явні позначення?

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

Тож обидва підходи протікають. Основна відмінність полягає в тому, що деструктори просочуються всюди в C ++, але IDisposeце дуже рідко в .NET.


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

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