RAII reprezintă "recuperarea resurselor este inițializarea". RAII este un model de design care utilizează codul C ++ pentru a elimina scurgeri de resurse. Diversificarea resurselor se produce atunci când o resursă pe care programul dvs. a dobândit-o nu este eliberată ulterior. Cel mai familiar exemplu este o scurgere de memorie. Deoarece C ++ nu are un GC așa cum procedează C #, trebuie să aveți grijă să vă asigurați că memoria alocată dinamic este eliberată. În caz contrar, veți pierde această memorie. De asemenea, scurgeri de resurse pot duce la incapacitatea de a deschide un fișier deoarece sistemul de fișiere consideră că este deja deschis, incapacitatea de a obține o blocare într-un program multi-threaded sau incapacitatea de a lansa un obiect COM.
RAII funcționează din cauza a trei fapte fundamentale.
RAII vă ajută să vă asigurați că veți elibera resurse, fără excepții, prin utilizarea pur și simplu a obiectelor de stocare automată care conțin resursele. Este similar cu combinația dintre System.IDisposable
interfață împreună cu instrucțiunea utilizată în C #. Odată ce execuția părăsește blocul curent, fie prin executare reușită sau prin excepție, resursele sunt eliberate.
Când vine vorba de excepții, un element cheie de reținut este faptul că numai obiecte construite complet sunt distruse. Dacă primiți o excepție în mijlocul unui constructor și ultimul bloc de încercări a început în afara acelui constructor, deoarece obiectul nu este construit în întregime, distrugătorul său nu va rula.
Aceasta nu înseamnă că variabilele membre, care sunt obiecte, nu vor fi distruse. Orice obiecte variabile membre care au fost construite integral în cadrul constructorului înainte ca excepția să aibă loc, sunt obiecte de durată automată construită integral. Astfel, acele obiecte membre vor fi distruse la fel ca orice alte obiecte construite complet.
De aceea ar trebui să puneți întotdeauna alocări dinamice în interiorul fiecăruia std :: unique_ptr
sau std :: shared_ptr
. Exemple de astfel de tipuri devin obiecte construite pe deplin atunci când alocarea reușește. Chiar dacă constructorul pentru obiectul pe care îl creați nu reușește în continuare, std :: unique_ptr
resursele vor fi eliberate de distrugătorul său și de std :: shared_ptr
resursele vor scădea numărul de referință și vor fi eliberate dacă numărătoarea devine zero.
RAII nu are legătură doar cu shared_ptr și unique_ptr, desigur. De asemenea, se aplică și altor tipuri de resurse, cum ar fi un obiect de fișier, unde achiziția este deschiderea fișierului, iar destructorul asigură că fișierul este închis corect. Acesta este un exemplu deosebit de bun, deoarece trebuie doar să creați acel cod chiar o dată - când scrieți clasa - mai degrabă decât din nou și din nou, ceea ce trebuie să faceți dacă scrieți logica apropiată în fiecare loc în care trebuie să deschideți fişier.
Utilizarea RAII este descrisă de numele său: Obținerea unei resurse dinamice ar trebui să finalizeze inițializarea unui obiect. Dacă urmați acest tip de resursă pe obiect, atunci este imposibil să încheiați cu o scurgere de resurse. Fie veți obține cu succes resursa, caz în care obiectul care o încapsulează va termina construcția și va fi supus distrugerii, fie încercarea de achiziție va eșua, caz în care nu ați achiziționat resursa; astfel, nu există nici o resursă pentru eliberare.
Distructorul unui obiect care încapsulează o resursă trebuie să elibereze acea resursă. Acest lucru, printre altele, este unul dintre motivele importante pentru care distrugătorii nu ar trebui să facă niciodată excepții, cu excepția celor pe care le captează și le manipulează în interiorul lor.
În cazul în care distrugătorul a aruncat o excepție neobișnuită, atunci, pentru a cita Bjarne Stroustrup, "Este posibil să se întâmple tot felul de lucruri rele, deoarece regulile de bază ale bibliotecii standard și limba în sine vor fi încălcate. Nu o faceți.
După cum a spus, nu o faceți. Asigurați-vă că știți ce excepții, dacă este cazul, ar putea să arunce tot ceea ce numiți în distrugătorii dvs., astfel încât să vă puteți asigura că le ocupați în mod corespunzător.
Acum s-ar putea să vă gândiți că dacă urmați acest model, veți ajunge să scrieți o grămadă de clase. Veți scrie ocazional o clasă suplimentară aici și acolo, dar nu este posibil să scrieți prea multe din cauza indicilor inteligenți. Indicatoarele inteligente sunt și obiecte. Cele mai multe tipuri de resurse dinamice pot fi introduse în cel puțin una din clasele inteligente de pointeri inteligente. Atunci când puneți o achiziție de resurse în interiorul unui pointer inteligent adecvat, în cazul în care achiziția reușește, atunci acel obiect indicator inteligent va fi construit pe deplin. Dacă apare o excepție, atunci destructorul inteligent al obiectului de pointer va fi apelat și resursa va fi eliberată.
Există câteva tipuri importante de pointeri inteligente. Să ne uităm la ele.
std :: unique_ptr
FuncţieIndicatorul unic, std :: unique_ptr
, este proiectat să păstreze un indicator la un obiect alocat dinamic. Ar trebui să utilizați acest tip numai atunci când doriți ca un obiect la obiect să existe. Este o clasă de șabloane care are un argument obligatoriu și unul opțional. Argumentul obligatoriu este tipul de pointer pe care îl va păstra. De exemplu auto rezultat = std :: unique_ptr
va crea un pointer unic care conține un int *. Argumentul opțional este tipul de ștergere. Vedem cum să scriem un ștergător într-un eșantion care vine. În mod obișnuit, puteți evita să specificați un ștergător deoarece elementul default_deleter, care vă este furnizat dacă nu este specificat niciun ștergător, acoperă aproape în fiecare caz în care vă puteți imagina.
O clasă care are std :: unique_ptr
ca o variabilă membru nu poate avea un constructor de copiere implicit. Semantica copiilor este dezactivată pentru std :: unique_ptr
. Dacă doriți ca un constructor de copiat într-o clasă care are un indicator unic, trebuie să-l scrieți. De asemenea, trebuie să scrieți o supraîncărcare pentru operatorul de copiere. În mod normal, vrei std :: shared_ptr
în acest caz.
Cu toate acestea, este posibil să aveți ceva asemănător unui set de date. De asemenea, puteți dori ca orice copie a clasei să creeze o copie a datelor așa cum există în acel moment. În acest caz, un pointer unic cu un constructor de copiere personalizat ar putea fi alegerea potrivită.
std :: unique_ptr
este definit în
std :: unique_ptr
are patru funcții membre de interes.
Funcția get membru returnează pointerul stocat. Dacă trebuie să apelați o funcție pe care trebuie să o introduceți, folosiți pentru a obține o copie a indicatorului.
Funcția membră de eliberare returnează și pointerul stocat, însă eliberarea invalidează unic_ptr în proces prin înlocuirea indicatorului stocat cu un pointer nul. Dacă aveți o funcție în care doriți să creați un obiect dinamic și apoi să îl returnați, în timp ce mențineți siguranța excepției, utilizați std: unique_ptr
pentru a stoca obiectul creat în mod dinamic și apoi pentru a returna rezultatul lansării apelului. Acest lucru vă oferă siguranță excepțională, permițându-vă în același timp să returnați obiectul dinamic fără să îl distrugeți std :: unique_ptr
atunci când comanda iese din funcție la revenirea valorii pointerului eliberat la sfârșit.
Funcția membră swap permite ca doi indicatori unici să își schimbe indicatorii memorați, deci dacă A menține un pointer la X și B menține un pointer la Y, rezultatul apelului A :: swap (B);
este că A va păstra acum un pointer la Y și B va ține un pointer la X. Delegații pentru fiecare vor fi de asemenea schimbate, deci dacă aveți un deleter personalizat pentru unul sau ambii indicatori unici, asigurați-vă că fiecare va își păstrează deleatorul asociat.
Funcția de resetare a elementului determină distrugerea în majoritatea cazurilor a obiectului indicat de indicatorul memorat, dacă este cazul. Dacă indicatorul curent stocat este nul, atunci nimic nu este distrus. Dacă treceți cu un pointer la obiectul la care indică indicatorul curent stocat, atunci nimic nu este distrus. Puteți alege să treceți într-un nou pointer, nullptr sau să apelați funcția fără parametri. Dacă introduceți un pointer nou, atunci acel obiect nou este stocat. Dacă treceți nullptr, atunci pointerul unic va stoca null. Apelarea funcției fără parametri este aceeași ca și apelarea cu nullptr.
std :: shared_ptr
FuncţieIndicatorul comun, std :: shared_ptr
, este proiectat să păstreze un pointer la un obiect alocat dinamic și să păstreze un număr de referință pentru el. Nu este magie; dacă creați doi indicatori partajați și le transmiteți câte un pointer la același obiect, veți ajunge cu două indicatoare comune - fiecare având un număr de referință de 1, nu 2. Prima care este distrusă va elibera resursa de bază, dând rezultate catastrofale atunci când încercați să utilizați cealaltă sau când celălalt este distrus și încearcă să elibereze resursa de bază care a fost deja lansată.
Pentru a utiliza în mod corespunzător indicatorul comun, creați o instanță cu un indicator de obiect și apoi creați toți ceilalți indicatori partajați pentru acel obiect de la un pointer comun existent, valabil pentru acel obiect. Acest lucru asigură un număr de referință comun, astfel încât resursa va avea o durată de viață corespunzătoare. Să examinăm o mostră rapidă pentru a vedea modalitățile corecte și greșite de a crea obiecte shared_ptr.
Mostră: SharedPtrSample \ SharedPtrSample.cpp
#include#include #include #include "... /pchar.h" folosind namespace std; (a), B (b) int A; (a), B (b) int A; int B; ; wostream & operator<<(wostream& stream, TwoInts* v) stream << v->A << L" " << v->B; fluxul de întoarcere; int _pmain (int / * argc * /, _pchar * / * argv * / []) / / Bad: duce la dublu liber. // încercați // // TwoInts * p_i = noi două elemente (10, 20); // auto sp1 = shared_ptr (P_i); // auto sp2 = shared_ptr (P_i); // p_i = nullptr; // wcout << L"sp1 count is " << sp1.use_count() << L"." << endl << // L"sp2 count is " << sp2.use_count() << L"." << endl; // //catch(exception& e) // // wcout << L"There was an exception." << endl; // wcout << e.what() << endl << endl; // //catch(… ) // // wcout << L"There was an exception due to a double free " << // L"because we tried freeing p_i twice!" << endl; // // This is one right way to create shared_ptrs. auto sp1 = shared_ptr (noi două elemente (10, 20)); auto sp2 = partajat_ptr (Sp1); wcout << L"sp1 count is " << sp1.use_count() << L"." << endl << L"sp2 count is " << sp2.use_count() << L"." << endl; wcout << L"sp1 value is " << sp1 << L"." << endl << L"sp2 value is " << sp2 << L"." << endl; // This is another right way. The std::make_shared function takes the // type as its template argument, and then the argument value(s) to the // constructor you want as its parameters, and it automatically // constructs the object for you. This is usually more memory- // efficient, as the reference count can be stored with the // shared_ptr's pointed-to object at the time of the object's creation. auto sp1 = make_shared (10, 20); auto sp2 = partajat_ptr (Sp1); wcout << L"sp1 count is " << sp1.use_count() << L"." << endl << L"sp2 count is " << sp2.use_count() << L"." << endl; wcout << L"sp1 value is " << sp1 << L"." << endl << L"sp2 value is " << sp2 << L"." << endl; return 0;
std :: shared_ptr
este definit în
std :: shared_ptr
are cinci funcții membre de interes.
Funcția get member funcționează la fel ca funcția std :: unique_ptr :: get member.
Funcția membru use_count returnează o lungime, ceea ce vă arată ce număr de referință curent este pentru obiectul țintă. Aceasta nu include referințe slabe.
Funcția de membru unic returnează o bool, informându-vă dacă acest pointer comun este unicul proprietar al obiectului țintă.
Funcția de swap membru funcționează la fel ca și std :: unique_ptr :: schimb
membră, cu adăugarea că numărul de referință al resurselor rămâne același.
Funcția de resetare a elementului reduce numărul de referință pentru resursa de bază și o distruge dacă numărul resurselor devine zero. Dacă un pointer către un obiect este trecut, indicatorul comun va stoca și va începe un nou număr de referință pentru acest indicator. Dacă nullptr este trecut în, sau dacă nu este trecut nici un parametru, atunci pointerul partajat va stoca null.
std :: make_shared
Funcţie std :: make_shared
funcția șablon este o modalitate convenabilă de a construi un inițial std :: shared_ptr
. După cum am văzut anterior SharedPtrSample
, treceți tipul ca argument pentru șablon și apoi introduceți pur și simplu argumentele, dacă există, pentru constructorul dorit. std :: make_shared
va construi o instanță heap a tipului de obiect argument șablon și o va face într-o std :: shared_ptr
. Apoi poți trece asta std :: shared_ptr
ca argument pentru std :: shared_ptr
constructor pentru a crea mai multe referințe la acel obiect partajat.
ComPtr
în WRL pentru aplicațiile Metro-StyleBiblioteca de șabloane pentru Windows Runtime (WRL) oferă un pointer inteligent numit ComPtr în spațiul de nume Microsoft :: WRL pentru a fi utilizat cu obiecte COM în aplicații în stilul metroului Windows 8. Pointerul se găsește în
Majoritatea funcționalităților sistemului de operare pe care le puteți utiliza în aplicațiile în stil Metro sunt expuse de Windows Runtime ("WinRT"). Obiectele WinRT oferă funcționalitatea lor automată de numărare a referințelor pentru crearea și distrugerea obiectelor. Unele funcționalități ale sistemului, cum ar fi Direct3D, necesită utilizarea și manipularea directă a acestora prin COM clasic. ComPtr se ocupă de numărarea de referință bazată pe IUnknown a COM pentru dvs. De asemenea, oferă pachete convenabile pentru QueryInterface și include alte funcții care sunt utile pentru indicatorii inteligenți.
Cele două funcții ale membrilor pe care le utilizați în mod obișnuit sunt As pentru a obține o interfață diferită pentru obiectul COM subiacent și Get to take a pointer de interfață la obiectul COM sub care se află ComPtr-ul (echivalentul lui std :: unique_ptr :: obține
).
Uneori veți folosi Detach, care funcționează la fel ca std :: unique_ptr :: release, dar are un nume diferit deoarece eliberarea în COM implică decrementarea numărului de referință și Detach nu face asta.
S-ar putea să utilizați ReleaseAndGetAddressOf pentru situațiile în care aveți un ComPtr existent care ar putea deține deja un obiect COM și doriți să îl înlocuiți cu un nou obiect COM de același tip. ReleaseAndGetAddressOf
face același lucru ca și funcția de membru GetAddressOf, dar mai întâi eliberează interfața sa de bază, dacă există.
Spre deosebire de .NET, unde toate excepțiile derivă din System.Exception și au metode și proprietăți garantate, excepțiile C ++ nu sunt necesare pentru a deriva din nimic; nici nu trebuie să fie chiar tipuri de clase. În C ++, aruncați L "Hello World!"; este perfect acceptabil pentru compilator ca este aruncarea 5 ;. Practic, excepțiile pot fi orice.
Acestea fiind spuse, mulți programatori C ++ vor fi nemulțumiți să vadă o excepție care nu derivă din std :: excepție
(găsite în std :: excepție
oferă o modalitate de a captura excepții de tip necunoscut și de a recupera informații de la aceștia prin intermediul funcției membrilor, înainte de a le arunca. std :: excepție :: Ce
nu ia parametri și returnează a const char * string
, pe care le puteți vizualiza sau înregistra, astfel încât să știți ce a cauzat excepția.
Nu există nici o urmă de stivă - fără a număra capabilitățile de urmărire a stivei pe care le oferă aplicația dvs. de depanare - cu excepția C ++. Deoarece obiectele de durată automată din sfera încercării care capturează excepția sunt distruse automat înainte ca activatorul de captură corespunzător, dacă există, să fie activat, nu aveți luxul de a examina datele care ar fi cauzat excepția. Tot ce trebuie să lucrați inițial este mesajul de la ce funcție membră.
Dacă este ușor să recreați condițiile care au condus la excepție, puteți seta un punct de întrerupere și repetați programul, permițându-vă să treceți prin execuția zonei de probleme și, eventual, să identificați problema. Deoarece acest lucru nu este întotdeauna posibil, este important să fii cât mai precis cu mesajul de eroare.
Când derivă din std :: excepție
, ar trebui să vă asigurați că ați suprasolicitat ceea ce funcția membru a furnizat un mesaj de eroare util care vă va ajuta pe dvs. și pe alți dezvoltatori să diagnosticați ce s-a întâmplat.
Unii programatori folosesc o variantă a unei reguli care să spună că ar trebui să arunci mereu std :: excepție
-excepții derivate. Amintindu-ne că punctul de intrare (principal sau wmain) returnează un număr întreg, acești programatori vor arunca std :: excepție
-derivă excepții atunci când codul lor se poate recupera, dar va arunca pur și simplu o valoare integrată bine definită dacă eșecul este nerecuperabil. Codul punctului de intrare va fi înfășurat într-un bloc de încercare care are o captură pentru un int. Operatorul de captură va returna valoarea int captată. În majoritatea sistemelor, o valoare de returnare de 0 de la un program înseamnă succes. Orice altă valoare înseamnă esec.
Dacă există o insuficiență catastrofică, aruncarea unei valori întregi bine definite, alta decât 0, poate contribui la o anumită semnificație. Dacă nu lucrați la un proiect în care acesta este stilul preferat, trebuie să vă lipiți std :: excepție
-excepții derivate, deoarece acestea permit programelor să se ocupe de excepții utilizând un sistem simplu de înregistrare pentru a înregistra mesaje de la excepții care nu sunt tratate și efectuează orice curățare care este sigură. Aruncarea ceva ce nu derivă din std :: excepție
ar interfera cu aceste mecanisme de logare a erorilor.
Un ultim lucru de remarcat este că construirea finală a lui C # nu are nici un echivalent în C ++. Idioma RAII, atunci când este implementată corect, o face inutilă, deoarece totul va fi curățat.
Am discutat deja std :: excepție
, dar există mai multe tipuri decât cele disponibile în biblioteca standard și există funcționalități suplimentare de explorat. Să ne uităm la funcționalitatea din
std :: termina
funcția implicită vă permite să vă dezactivați din orice aplicație. Ar trebui să fie folosită cu ușurință, deoarece apelarea acesteia, mai degrabă decât aruncarea unei excepții, va trece peste toate mecanismele normale de tratare a excepțiilor. Dacă doriți, puteți scrie o funcție terminată personalizată fără parametri și valori returnate. Un exemplu de acest lucru va fi văzut în ExceptionsSample, care vine.
Pentru a seta terminarea particularizată, apelați std :: set_terminate
și să-i transmiteți adresa funcției. Aveți posibilitatea să modificați manualul de terminare personalizat în orice moment; ultimul set de funcții este ceea ce se va numi în cazul unui apel la std :: termina
sau o excepție nefolosită. Managerul implicit apelează funcția de întrerupere de la
std :: excepție
. Aceste două clase servesc ca clasă părinte pentru mai multe alte clase.
std :: runtime_error
clasa este clasa părinte pentru excepțiile aruncate de runtime sau din cauza unei greșeli într-o funcție C ++ Standard Library. Copiii săi sunt std :: overflow_error
clasa, std :: range_error
clasa, și std :: underflow_error
clasă.
std :: logic_error
clasa este clasa părinte pentru excepțiile aruncate din cauza erorii programatorului. Copiii săi sunt std :: domain_error
clasa, std :: invalid_argument
clasa, std :: length_error
clasa, și std :: out_of_range
clasă.
Puteți obține din aceste clase sau creați propriile clase de excepție. Vinerea cu o ierarhie excepțională este o sarcină dificilă. Pe de o parte, doriți excepții care vor fi destul de specifice încât să puteți face față tuturor excepțiilor bazate pe cunoștințele dvs. la timpul de construcție. Pe de altă parte, nu doriți o clasă de excepție pentru fiecare eroare care ar putea apărea. Codul tău s-ar sfârși prin umflături și greutăți, ca să nu mai vorbim de pierderea timpului de scriere a sistemelor de gestionare a capturilor pentru fiecare clasă de excepții.
Petreceți timp la o tablă albă sau cu un stilou și hârtie sau, totuși, doriți să vă gândiți la exemplul de excepție al aplicației dvs..
Următorul eșantion conține o clasă numită InvalidArgumentExceptionBase
, care este folosit ca părinte al unei clase de șabloane numite InvalidArgumentException
. Combinația dintre o clasă de bază care poate fi prinsă cu un handler de excepție și o clasă de șablon care ne permite să personalizăm diagnosticele de ieșire bazate pe tipul parametrului este o opțiune pentru echilibrarea între specializare și bloat de cod.
Clasa de șabloane ar putea părea confuză chiar acum; vom discuta șabloanele într-un capitol viitoare, moment în care orice lucru necunoscut în prezent ar trebui clarificat.
Exemplu: ExceptionsSample \ InvalidArgumentException.h
#pragma o dată #include#include #include #include spațiu de nume CppForCsExceptions class InvalidArgumentExceptionBase: public std :: invalid_argument public: InvalidArgumentExceptionBase (void): std :: invalid_argument ("") virtual ~ InvalidArgumentExceptionBase (void) () suprascrie = 0; ; șablon class InvalidArgumentException: public InvalidArgumentExceptionBase public: inline InvalidArgumentException (const char * className, const char * functionSignature, const char * parameterName, T parameterValue); inline virtual ~ InvalidArgumentException (void) arunca (); în linie virtuală const char * ce (void) const arunca () suprascrie; private: std :: șir m_whatMessage; ; șablon InvalidArgumentException :: InvalidArgumentException (const char * className, const char * functionSignature, const char * parameterName, T parametruValue): InvalidArgumentExceptionBase (), m_whatMessage () std :: stringstream msg; msg << className << "::" << functionSignature << " - parameter '" << parameterName << "' had invalid value '" << parameterValue << "'."; m_whatMessage = std::string(msg.str()); template InvalidArgumentException :: ~ InvalidArgumentException (void) throw () șablon const char * InvalidArgumentException :: ce (void) const arunca () return m_whatMessage.c_str ();
Exemplu: ExcepțiiExemplu \ ExcepțiiSample.cpp
#include#include #include #include #include #include #include #include #include "InvalidArgumentException.h" #include "... /pchar.h" folosind spațiul de nume CppForCsExceptions; folosind namespace std; clasa ThrowClass public: ThrowClass (void): m_shouldThrow (false) wcout << L"Constructing ThrowClass." << endl; explicit ThrowClass(bool shouldThrow) : m_shouldThrow(shouldThrow) wcout << L"Constructing ThrowClass. shouldThrow = " << (shouldThrow ? L"true." : L"false.") << endl; if (shouldThrow) throw InvalidArgumentException ("ThrowClass", "ThrowClass (bool shouldThrow)", "shouldThrow", "true"); ~ ThrowClass (void) wcout << L"Destroying ThrowClass." << endl; const wchar_t* GetShouldThrow(void) const return (m_shouldThrow ? L"True" : L"False"); private: bool m_shouldThrow; ; class RegularClass public: RegularClass(void) wcout << L"Constructing RegularClass." << endl; ~RegularClass(void) wcout << L"Destroying RegularClass." << endl; ; class ContainStuffClass public: ContainStuffClass(void) : m_regularClass(new RegularClass()), m_throwClass(new ThrowClass()) wcout << L"Constructing ContainStuffClass." << endl; ContainStuffClass(const ContainStuffClass& other) : m_regularClass(new RegularClass(*other.m_regularClass)), m_throwClass(other.m_throwClass) wcout << L"Copy constructing ContainStuffClass." << endl; ~ContainStuffClass(void) wcout << L"Destroying ContainStuffClass." << endl; const wchar_t* GetString(void) const return L"I'm a ContainStuffClass."; private: unique_ptr m_regularClass; shared_ptr m_throwClass; ; void TerminateHandler (void) wcout << L"Terminating due to unhandled exception." << endl; // If you call abort (from ), programul va ieși din sistem // anormal. De asemenea, va ieși anormal dacă nu apelați // nimic pentru a determina ieșirea din această metodă. abort (); //// Dacă ați fi apelat în loc de ieșire (0) (și de la ), // / atunci programul dvs. va ieși ca și cum nimic nu ar fi // // greșit. Acest lucru este rău pentru că ceva nu merge bine. //// Prezint acest lucru pentru a sti ca este posibil ca //// un program sa arunce o exceptie neconditionata si inca // / sa iesi intr-un mod care nu este interpretat ca un accident, de cand //// este posibil să trebuiască să aflați de ce un program continuă brusc // // ieșind încă nu se prăbușește. Aceasta ar fi o astfel de cauză //// pentru asta. // exit (0); int _pmain (int / * argc * /, _pchar * / * argv * / []) // Setați un handler personalizat pentru std :: terminate. Rețineți că acest handler // nu se va executa decât dacă îl executați dintr-un prompt de comandă. Debuggerul // va intercepta excepția nefolosită și vă va prezenta opțiunile de depanare // atunci când îl executați din Visual Studio. set_terminate (& TerminateHandler); încercați ContainStuffClass cSC; wcout << cSC.GetString() << endl; ThrowClass tC(false); wcout << L"tC should throw? " << tC.GetShouldThrow() << endl; tC = ThrowClass(true); wcout << L"tC should throw? " << tC.GetShouldThrow() << endl; // One downside to using templates for exceptions is that you need a // catch handler for each specialization, unless you have a base // class they all inherit from, that is. To avoid catching // other std::invalid_argument exceptions, we created an abstract // class called InvalidArgumentExceptionBase, which serves solely to // act as the base class for all specializations of // InvalidArgumentException . Acum putem să le prindem pe toți, dacă se dorește, // fără a avea nevoie de un handler de prindere pentru fiecare. Dacă ați fi dorit, totuși, ați putea avea în continuare un handler pentru o anumită specializare. captură (InvalidArgumentExceptionBase & e) wcout << L"Caught '" << typeid(e).name() << L"'." << endl << L"Message: " << e.what() << endl; // Catch anything derived from std::exception that doesn't already // have a specialized handler. Since you don't know what this is, you // should catch it, log it, and re-throw it. catch (std::exception& e) wcout << L"Caught '" << typeid(e).name() << L"'." << endl << L"Message: " << e.what() << endl; // Just a plain throw statement like this is a re-throw. throw; // This next catch catches everything, regardless of type. Like // catching System.Exception, you should only catch this to // re-throw it. catch (… ) wcout << L"Caught unknown exception type." << endl; throw; // This will cause our custom terminate handler to run. wcout << L"tC should throw? " << ThrowClass(true).GetShouldThrow() << endl; return 0;
Deși o menționez în comentariile, am vrut doar să subliniez din nou că nu veți vedea funcția de terminare personalizată execută dacă nu executați această mostră dintr-un prompt de comandă. Dacă o rulați în Visual Studio, debuggerul va intercepta programul și va orchestra propriul reziliu, după ce vă va da o șansă să examinați statul pentru a vedea dacă puteți determina ce a mers prost. De asemenea, rețineți că acest program se va prăbuși mereu. Acest lucru este de design, deoarece vă permite să vedeți manipulatorul terminat în acțiune.
După cum am văzut în acest articol, RAII vă ajută să vă asigurați că eliberați resursele, fără excepții, pur și simplu folosind obiectele de stocare automată care conțin resursele. În următoarea tranșă a acestei serii, mărim indicatorii și referințele.
Această lecție reprezintă un capitol din C ++ Succinctly, o carte electronică gratuită de la echipa de la Syncfusion.