Ресурстарды сатып алу инициализация болып табылады - Resource acquisition is initialization

Ресурстарды сатып алу инициализация болып табылады (RAII)[1] Бұл бағдарламалау идиомасы[2] бірнеше қолданылған объектіге бағытталған, статикалық терілген белгілі бір тілдік әрекетті сипаттайтын бағдарламалау тілдері. RAII-де ресурстарды ұстау а инвариант, және байланысты объектінің қызмет ету мерзімі: ресурстарды бөлу (немесе иемдену) объектіні құру кезінде жасалады (инициализация), арқылы конструктор, ресурстарды бөлу (босату) объектіні жою кезінде (нақты аяқтау) кезінде, ал деструктор. Басқаша айтқанда, инициализация сәтті болу үшін ресурстарды сатып алу сәтті болуы керек. Осылайша, ресурс инициализация аяқталған мен аяқталған кезде басталады (ресурстарды ұстап тұру класс инвариантты) арасында болады және объект тірі болғанда ғана сақталады. Егер объектінің ағып кетуі болмаса, жоқ ресурстардың ағып кетуі.

RAII ең танымал болып табылады C ++ ол қайдан пайда болды, сонымен қатар Д., Ада, Вала,[дәйексөз қажет ] және Тот.[3] Техника арналған ерекше-қауіпсіз ресурстарды басқару C ++ тілінде[4] 1984–89 жылдар аралығында, бірінші кезекте Bjarne Stroustrup және Эндрю Кениг,[5] және бұл терминнің өзін Stroustrup ұсынған.[6] RAII әдетте an ретінде оқылады инициализм, кейде «R, A, қос I» болып оқылады.[7]

Бұл фразеологизмнің басқа атаулары да бар Конструктор сатып алады, деструктор шығарады (CADRe)[8] және белгілі бір пайдалану стилі деп аталады Ауқымды ресурстарды басқару (SBRM).[9] Бұл соңғы термин ерекше жағдайға арналған автоматты айнымалылар. RAII ресурстарды объектімен байланыстырады өмір кезеңі, бұл ауқымның кіруімен және шығуымен сәйкес келмеуі мүмкін. (Тегін дүкенде бөлінген айнымалылардың өмір сүру ұзақтығы кез-келген ауқыммен байланысты емес.) Алайда автоматты айнымалылар үшін RAII пайдалану (SBRM) - бұл ең көп қолданылатын жағдай.

C ++ 11 мысалы

Келесісі C ++ 11 мысал, файлға қол жеткізу үшін RAII-ді қолдануды көрсетеді мутекс құлыптау:

# қосу <fstream># қосу <iostream># қосу <mutex># қосу <stdexcept># қосу <string>жарамсыз WriteToFile(const std::жіп& хабар) {  // | мутекс | | файлына қол жетімділікті қорғау болып табылады (бұл ағындар бойынша ортақ).  статикалық std::мутекс мутекс;  // Lock | mutex | | файлына қол жеткізер алдында.  std::lock_guard<std::мутекс> құлыптау(мутекс);  // Файлды ашып көріңіз.  std::ағын файл(«example.txt»);  егер (!файл.ашық_()) {    лақтыру std::runtime_error(«файлды аша алмады»);  }  // жазу | хабарлама | файлға |.  файл << хабар << std::соңы;  // | файл | ауқымнан шыққан кезде алдымен жабылады (ерекшелікке қарамастан)  // аумақтан шыққан кезде мутекс екінші рет құлыптан босатылады (блоктаушы деструктордан)  // (ерекшелікке қарамастан).}

Бұл код айрықша қауіпсіз, өйткені C ++ барлық стек нысандары қоршау аясының соңында жойылатындығына кепілдік береді, стек босату. Екеуінің де деструкторлары құлыптау және файл объектілер функциядан оралғанда, ерекше жағдай жасалғанына қарамастан шақырылғанына кепілдік беріледі.[10]

Жергілікті айнымалылар бір функция шеңберінде бірнеше ресурстарды оңай басқаруға мүмкіндік береді: олар құрастырылуының кері тәртібімен жойылады, ал объект толығымен тұрғызылған жағдайда ғана бұзылады, яғни оның конструкторынан ешқандай ерекшелік таралмаса.[11]

RAII пайдалану ресурстарды басқаруды едәуір жеңілдетеді, кодтың жалпы көлемін азайтады және бағдарламаның дұрыстығын қамтамасыз етуге көмектеседі. RAII сондықтан салалық стандартты нұсқаулармен ұсынылады,[12]және C ++ стандартты кітапханасының көп бөлігі идиоманы ұстанады.[13]

Артықшылықтары

Ресурстарды басқару әдістемесі ретінде RAII-дің артықшылықтары - бұл инкапсуляцияны қамтамасыз етеді, ерекшелік қауіпсіздігі (стек ресурстары үшін), және локалдылық (бұл логиканы алу мен босатуды қатар жазуға мүмкіндік береді).

Инкапсуляция қамтамасыз етілген, себебі ресурстарды басқару логикасы әр қоңырау сайтында емес, сыныпта бір рет анықталады. Ерекше қауіпсіздік стек ресурстарына (олар алынған көлемде шығарылатын ресурстарға) ресурстарды стек айнымалысының қызмет ету мерзіміне (белгілі бір көлемде жарияланған жергілікті айнымалы) байлап беру арқылы қамтамасыз етіледі: егер ерекшелік лақтырылды, және ерекше жағдайларды өңдеу дұрыс, тек токтан шыққан кезде орындалатын жалғыз код ауқымы сол көлемде жарияланған объектілерді деструкторлар болып табылады. Сонымен, анықтаманың локальділігі конструктор мен деструктор анықтамаларын сынып анықтамасында қатар жазу арқылы қамтамасыз етіледі.

Автоматты түрде бөлу және қалпына келтіру үшін ресурстарды басқару қолайлы объектілердің қызмет ету мерзіміне байланысты болуы керек. Ресурстар инициализация кезінде, егер оларды қол жетімді болғанға дейін пайдалану мүмкіндігі болмаса, сатып алынады және сол объектілерді бұзумен босатылады, бұл қателер болған жағдайда да орын алуы мүмкін.

RAII-мен салыстыру ақыры Java-да қолданылатын конструкцияны Stroustrup «шынайы жүйелерде ресурстардың түрлеріне қарағанда әлдеқайда көп ресурстарды сатып алу бар, сондықтан« ресурстарды сатып алу инициализациялау »техникасы« ақырғы »конструкцияны қолданудан гөрі аз код әкеледі» деп жазды.[1]

Әдеттегі пайдалану

RAII дизайны бақылау үшін жиі қолданылады мутекс құлыптар көп бұрандалы қосымшалар. Бұл жағдайда объект жойылған кезде құлыпты босатады. Бұл сценарийде RAII болмаса мүмкін тығырық мутексті құлыптау логикасы жоғары болар еді, оны ашу логикасынан алыс болар еді. RAII кезінде мутексті құлыптайтын код негізінен құлыптың орындалуы RAII нысанының аясынан шыққан кезде босатылатын логиканы қамтиды.

Тағы бір типтік мысал - файлдармен өзара әрекеттесу: бізде жазу үшін ашық файлды ұсынатын объект болуы мүмкін, онда файл конструкторда ашылады және орындалу объектінің ауқымын қалдырғанда жабылады. Екі жағдайда да RAII қарастырылып отырған ресурстардың тиісті түрде шығарылуын қамтамасыз етеді; ерекшелік қауіпсіздігін сақтау үшін әлі де сақ болу керек. Егер деректер құрылымын немесе файлын өзгертетін код ерекше қауіпсіз болмаса, мутекс құлпын ашуы немесе файл құрылымымен немесе файлмен зақымдалуы мүмкін.

Динамикалық бөлінген объектілерге меншік (бөлінген жады жаңа C ++ тілінде) RAII көмегімен басқарылуы мүмкін, мысалы, объект RAII (стекке негізделген) объект жойылған кезде босатылады. Осы мақсатта C ++ 11 стандартты кітапхана анықтайды ақылды нұсқағыш сыныптар std :: unique_ptr жеке меншіктегі объектілер үшін және std :: shared_ptr ортақ меншіктегі объектілер үшін. Осындай сабақтар арқылы да қол жетімді std :: auto_ptr C ++ 98 және boost :: shared_ptr ішінде Кітапханаларды көбейту.

Компилятордың «тазарту» кеңейтімдері

Екеуі де Қоңырау және GNU Compiler коллекциясы стандартты емес кеңейтуді жүзеге асырады C RAII қолдайтын тіл: «тазарту» айнымалы атрибуты.[14] Келесісі макро берілген деструктор функциясы бар айнымалыға түсініктеме береді, ол айнымалы шеңберден шыққан кезде шақырады:

статикалық кезекте жарамсыз fclosep(ФАЙЛ **фп) { егер (*фп) fclose(*фп); }#define _cleanup_fclose_ __attribute __ ((тазарту (fclosep)))

Осы макросты келесідей пайдалануға болады:

жарамсыз мысал_қолдану() {  _cleanup_fclose_ ФАЙЛ *журнал = фопен(«logfile.txt», «w +»);  fputs(«сәлем!», журнал);}

Бұл мысалда компилятор fclosep шақырылатын функция журнал бұрын мысал_қолдану қайтарады.

Шектеулер

RAII тек стек-бөлінген объектілер сатып алған және шығарған (тікелей немесе жанама) ресурстар үшін жұмыс істейді болып табылады Белгіленген статикалық объектінің қызмет ету мерзімі.Өздері ресурстарды алатын және босататын, үйінділермен бөлінген нысандар көптеген тілдерде, соның ішінде C ++ тілінде кең таралған. RAII үйіндіге негізделген объектілерге ресурстарды шығаратын деструкторды (немесе эквивалентті) іске қосу үшін барлық ықтимал орындау жолдары бойынша жасырын немесе анық жойылатындығына байланысты.[15]:8:27 Бұған қолдану арқылы қол жеткізуге болады ақылды көрсеткіштер барлық үйінді нысандарын басқару, циклдік сілтемелерге арналған әлсіз көрсеткіштермен.

C ++ тілінде стек шешімі тек ерекшелік бір жерде болған жағдайда ғана пайда болады. Себебі «егер бағдарламада сәйкестендіргіш табылмаса, онда terminate () функциясы шақырылады; стаканы тоқтату () үшін осы стаканың ашылмағанына қарамастан ашылмайды (15.5.1).» (C ++ 03 стандарты, §15.3 / 9).[16] Бұл мінез-құлық әдетте қабылданады, өйткені амалдық жүйе жад, файлдар, розеткалар және т.с.с қалған ресурстарды бағдарлама аяқталған кезде шығарады.[дәйексөз қажет ]

Анықтамалық санау

Перл, Python (ішінде CPython жүзеге асыру),[17] және PHP[18] объектінің қызмет ету мерзімін басқару анықтамалық санау, бұл RAII пайдалануға мүмкіндік береді. Енді сілтеме жасалмаған нысандар дереу жойылады немесе аяқталады және босатылады, сондықтан а деструктор немесе финалдаушы сол уақытта ресурстарды шығара алады. Алайда, мұндай тілдерде ол әрдайым идиомалық бола бермейді және Python-да арнайы ұсынылмайды (пайдасына контекст-менеджерлер және финализаторлар бастап әлсіз пакет).

Алайда, объектінің өмір сүру уақыты қандай-да бір ауқыммен байланысты емес, ал объектілер детерминирленбестен жойылуы немесе мүлдем болмауы мүмкін. Бұл кейбір ауқымның соңында босатылуы керек ресурстарды кездейсоқ ағып кетуге мүмкіндік береді. А сақталған нысандар статикалық айнымалы (атап айтқанда а ғаламдық айнымалы ) бағдарлама аяқталған кезде аяқталмауы мүмкін, сондықтан олардың ресурстары босатылмайды; Мысалы, CPython мұндай нысандарды аяқтауға кепілдік бермейді. Әрі қарай, дөңгелек сілтемелері бар объектілер қарапайым анықтамалық есептегіш арқылы жинақталмайды және ұзақ уақыт өмір сүреді; жиналған болса да (қоқысты неғұрлым күрделі жинау арқылы), жою уақыты мен жойылу тәртібі детерминистік емес болады. CPython-да циклдарды анықтайтын және циклдегі объектілерді аяқтайтын цикл детекторы бар, бірақ CPython 3.4-ке дейін циклдар жиналмайды, егер циклдегі кез-келген объектіде пысықтаушы болса.[19]

Әдебиеттер тізімі

  1. ^ а б Stroustrup, Bjarne (2017-09-30). «Неліктен C ++» ақырғы «құрылымды ұсынбайды?». Алынған 2019-03-09.
  2. ^ Саттер, шөп; Александреску, Андрей (2005). C ++ кодтау стандарттары. C ++ тереңдік сериясы. Аддисон-Уэсли. б.24. ISBN  978-0-321-11358-0.
  3. ^ «RAII - тот мысалы бойынша». doc.rust-lang.org. Алынған 2020-11-22.
  4. ^ Stroustrup 1994 ж, 16.5 Ресурстарды басқару, 388–89 бб.
  5. ^ Stroustrup 1994 ж, 16.1 Ерекше жағдайларды өңдеу: кіріспе, 383–84 бб.
  6. ^ Stroustrup 1994 ж, б. 389. Мен бұл техниканы «ресурстарды сатып алу инициализациялау» деп атадым.
  7. ^ Майкл Бурр (2008-09-19). «RAII қалай оқылады?». Stack overflow. Алынған 2019-03-09.
  8. ^ Артур Чайковский (2012-11-06). «Ресми RAII-ді CADRe-ге өзгерту». ISO C ++ стандарты - болашақ ұсыныстар. Google топтары. Алынған 2019-03-09.
  9. ^ Чоу, Аллен (2014-10-01). «Ресурстарды қолдану аясына негізделген (RAII)». Алынған 2019-03-09.
  10. ^ «Сәтсіздікке ұшыраған деструктормен қалай күресуге болады?». Стандартты C ++ қоры. Алынған 2019-03-09.
  11. ^ Ричард Смит (2017-03-21). «Жұмыс жобасы, C ++ бағдарламалау стандарттары» (PDF). Алынған 2019-03-09.
  12. ^ Stroustrup, Bjarne; Саттер, шөп (2020-08-03). «C ++ негізгі нұсқаулары». Алынған 2020-08-15.
  13. ^ «Менде блоктар тым көп; бұл үшін не істеуге болады?». Стандартты C ++ қоры. Алынған 2019-03-09.
  14. ^ «Айнымалылар атрибуттарын көрсету». GNU Compiler Collection (GCC) пайдалану. GNU жобасы. Алынған 2019-03-09.
  15. ^ Веймер, Уэстли; Некула, Джордж С. (2008). «Ерекше жағдайлар және бағдарламаның сенімділігі» (PDF). Бағдарламалау тілдері мен жүйелері бойынша ACM транзакциялары. 30 (2).
  16. ^ ildjarn (2011-04-05). «RAII және стек шешімі». Stack overflow. Алынған 2019-03-09.
  17. ^ «Python-ді C немесе C ++ көмегімен кеңейту: Анықтама саны». Python аудармашысын кеңейту және енгізу. Python бағдарламалық қамтамасыздандыру қоры. Алынған 2019-03-09.
  18. ^ хоббалар (2011-02-08). «PHP RAII үлгісін қолдай ма? Қалай?». Алынған 2019-03-09.
  19. ^ «gc - қоқыс жинаушының интерфейсі». Python стандартты кітапханасы. Python бағдарламалық қамтамасыздандыру қоры. Алынған 2019-03-09.

Әрі қарай оқу

Сыртқы сілтемелер