Operatoriaus pasirinkimas ir atminties valdymas C++
Pasirinktinis įgyvendinimas naujas ir ištrinti operatoriai C++ suteikia didžiulę atminties valdymo laisvę. Šie operatoriai suteikia kūrėjams galimybę valdyti atminties paskirstymą ir perskirstymą savo klasėse. Subklasifikavimas gali sukelti painiavą, ypač renkantis ištrinti operatorius objekto sunaikinimui.
Operatoriaus perkrovos C++ atveju pasirenkamas teisingas naujas operatorius atrodo paprastas, nes tikroji klasė yra žinoma paskirstymo metu. Tačiau tinkamo ištrynimo operatoriaus pasirinkimas gali būti subtilesnis, ypač kai pagrindinės klasės rodyklė susieja su išvestinės klasės egzemplioriumi.
Kai pagrindinės klasės žymeklis ištrina išvestinį klasės objektą, ar C++ naudoja ištrinti operatorius iš bazinės ar išvestinės klasės? Šis sprendimas turi didelę įtaką atminties valdymui ir atlaisvinimui, ypač klasėse su unikaliais atminties valdymo algoritmais.
Šiame straipsnyje tiriame, kaip g++ tvarko ištrynimo operatoriaus pasirinkimą, kai poklasiai jį nepaiso. Naudosime pavyzdį, kad parodytume, kaip C++ vykdymo laikas nusprendžia, kurią formą ištrinti yra naudojamas ir kaip tai veikia atminties valdymą praktikoje.
| komandą | Naudojimo pavyzdys |
|---|---|
| operator delete | Tai yra pritaikytas trynimo operatoriaus įgyvendinimas. C++ kalboje galite nepaisyti operatorius ištrinti kad sukurtumėte priskirtą atminties išskyrimo elgseną savo klasei. Kaip matyti iš scenarijaus, atmintis yra aiškiai atlaisvinama naudojant std::free(ptr). |
| operator new | Panašiai kaip operatorius ištrinti, šis pritaikytas įgyvendinimas operatorius naujas leidžia nustatyti tinkintą atminties paskirstymo elgesį. Jis buvo naudojamas atminčiai paskirstyti naudojant std::malloc(size) ir išsiųsti pasirinktinį pranešimą, nurodantį, kuriai klasei skirta atmintis. |
| virtual destructor | Kai objektui ištrinti naudojate bazinės klasės žymeklį, virtualus destruktorius iškviečia atitinkamą naikintoją. Pavyzdyje tiek X, tiek ArenaAllocatedX naudoja virtualius naikintuvus, kad tinkamai valdytų atminties paskirstymą. |
| gtest | The gtest sistema (GoogleTest) naudojama vienetų testams kurti. Tokiu atveju ji patikrina, ar teisinga ištrinti naudojamas operatorius. Labai svarbu užtikrinti, kad atminties paskirstymo ir atskyrimo veiksmai būtų plačiai išbandyti įvairiuose scenarijuose. |
| ASSERT_EQ | Ši makrokomanda iš gtest biblioteka patikrina, ar dvi reikšmės yra lygios, o tai dažniausiai naudojama testavimo kode. Nors šiuo atveju supaprastinta, ji gali būti naudojama atminties būsenoms ar trynimo procesams palyginti sudėtingesnio testavimo metu. |
| vptr | Vptr yra paslėpta rodyklė, pridėta prie klasių su virtualiomis funkcijomis. Jis nurodo virtualią lentelę (VTable), kurioje yra virtualių funkcijų adresai. Supratimas vptr paaiškina, kodėl iškviečiamas atitinkamas trynimo operatorius, atsižvelgiant į dinaminį objekto tipą. |
| VTable | A V lentelė (Virtual Table) yra struktūra, kuri palaiko nuorodas į virtualias funkcijas kiekvienai klasei su virtualiais metodais. Tai labai svarbu nustatant tinkamą naikinimo operatorių mūsų scenarijaus išvestinėms klasėms. |
| malloc | The malloc funkcija dinamiškai paskirsto atmintį. Pasirinktinis operatorius naujas buvo naudojamas vietoj naujos siekiant pabrėžti tiesioginį atminties valdymą ir suteikti daugiau lankstumo testuojant skirtingus paskirstymo algoritmus. |
Atminties valdymas ir operatoriaus ištrynimas C++
Anksčiau pasiūlytuose scenarijuose dėmesys sutelkiamas į tai, kaip C++ nustato tinkamą ištrinti operatorius dirbant su poklasio objektais. C++ leidžia perkrauti naujas ir ištrinti operatoriai gali tvarkyti pasirinktinius atminties paskirstymo ir paskirstymo algoritmus. Tai aktualu tais atvejais, kai poklasiams gali būti taikomi kitokie atminties valdymo reikalavimai nei jų bazinėms klasėms. Scenarijų pavyzdžiai tai parodo sukurdami bazinę klasę X ir poklasis ArenaPaskirtaX, tiek su pasirinktiniu įgyvendinimu naujas ir ištrinti operatoriai.
Pirmajame scenarijuje naujas ir ištrinti operatoriai yra perkrauti, kad pateiktų nurodytus pranešimus atminties paskirstymo ir atlaisvinimo metu. Bazinė klasė X turi vieną įgyvendinimą, bet poklasį ArenaPaskirtaX nepaiso jo. Svarbiausia yra tai, kaip C++ nusprendžia, kuri versija ištrinti operatorius, kurį reikia naudoti, kai objektas sunaikinamas. Tinkamas operatorius iškviečiamas abiem X ir ArenaPaskirtaX, nes tai lemia dinaminis objekto tipas, o ne rodyklės tipas (tai yra X*).
Antrasis scenarijus pristato sąvoką vptr ir V lentelė. Tai labai svarbu norint suprasti, kaip C++ siunčia virtualias funkcijas, įskaitant destruktorius. Nors ištrynimo operatorius nėra įtrauktas į VTable, virtualus naikintuvas atlieka lemiamą vaidmenį užtikrinant, kad būtų iškviestas tinkamas trynimo operatorius, atsižvelgiant į objekto dinaminį tipą. Destruktorius garantuoja, kad kai a X* rodyklė nurodo a ArenaPaskirtaX objektas, poklasis ištrinti operacija vadinama.
Galiausiai galutinis scenarijus prideda vienetų testus naudojant „GoogleTest“ sistemą. Vienetų testavimas yra labai svarbus siekiant užtikrinti, kad įvairiuose kontekstuose būtų vykdomos tinkamos atminties valdymo funkcijos. Mes naudojame ASSERT_EQ užtikrinti, kad tiek bazinis, tiek poklasis teisingai paskirstytų ir ištrintų atmintį naudodami atitinkamus operatorius. Tai padeda užtikrinti, kad neatsirastų atminties nutekėjimo ar netinkamo atlaisvinimo, o tai labai svarbu programoms, kurios labai priklauso nuo dinaminio atminties valdymo, ypač programinėje įrangoje, kuriai reikalinga didelė sparta.
Apskritai šie scenarijai parodo, kaip C++ apdoroja operatoriaus perkrovą, taip pat pabrėžia virtualių naikintuvų ir dinaminio tipo nustatymo poreikį valdant atmintį paveldėjimo hierarchijose. Suprasti VTable mechaniką ir vaidmenį vptr paaiškina, kodėl tinka ištrinti operatorius pasirenkamas vykdymo metu, užtikrinant tinkamą atminties tvarkymą tiek pagrindinėje, tiek sudėtingoje klasių hierarchijoje.
Atminties valdymas ir operatoriaus ištrynimas C++
Šis scenarijus naudoja gryną C++ metodą, kad ištirtų, kaip parenkamas trynimo operatorius, kai poklasiai jį nepaiso. Mes išbandome alternatyvias operatoriaus perkrovas klasėje ir poklasiuose su tinkamais atminties valdymo mechanizmais.
#include <iostream>#include <cstdlib>struct X {void* operator new(std::size_t size) {std::cout << "new X\n";return std::malloc(size);}void operator delete(void* ptr) {std::cout << "delete X\n";std::free(ptr);}virtual ~X() = default;};struct ArenaAllocatedX : public X {void* operator new(std::size_t size) {std::cout << "new ArenaAllocatedX\n";return std::malloc(size);}void operator delete(void* ptr) {std::cout << "delete ArenaAllocatedX\n";std::free(ptr);}};int main() {X* x1 = new X();delete x1;X* x2 = new ArenaAllocatedX();delete x2;return 0;}
VTable tyrinėjimas C++, skirtas operatoriaus ištrynimui
Šis scenarijus generuoja virtualias lenteles ir naudoja virtualius destruktorius, kad nustatytų, kaip pasirenkami trynimo operatoriai. VTable struktūrai pamatyti naudojamos g++ kompiliatoriaus vėliavėlės ir specifiniai atminties tvarkymo įrankiai.
#include <iostream>#include <cstdlib>struct X {virtual ~X() { std::cout << "X destructor\n"; }static void operator delete(void* ptr) {std::cout << "delete X\n";std::free(ptr);}};struct ArenaAllocatedX : public X {virtual ~ArenaAllocatedX() { std::cout << "ArenaAllocatedX destructor\n"; }static void operator delete(void* ptr) {std::cout << "delete ArenaAllocatedX\n";std::free(ptr);}};int main() {X* x1 = new X();delete x1;X* x2 = new ArenaAllocatedX();delete x2;return 0;}
Atminties tvarkymo vienetų testai C++
Šis scenarijus pateikia vienetų testus tiek atminties paskirstymo, tiek ištrynimo scenarijuose, remiantis C++ testavimo sistemomis, tokiomis kaip GoogleTest, kad būtų užtikrinta, jog operatoriaus ištrynimo metodai bus tinkamai iškviesti.
#include <iostream>#include <gtest/gtest.h>struct X {void* operator new(std::size_t size) {return std::malloc(size);}void operator delete(void* ptr) {std::free(ptr);}virtual ~X() = default;};struct ArenaAllocatedX : public X {void* operator new(std::size_t size) {return std::malloc(size);}void operator delete(void* ptr) {std::free(ptr);}virtual ~ArenaAllocatedX() = default;};TEST(MemoryTest, DeleteX) {X* x = new X();delete x;ASSERT_EQ(1, 1); // Simplified check}TEST(MemoryTest, DeleteArenaAllocatedX) {X* x = new ArenaAllocatedX();delete x;ASSERT_EQ(1, 1); // Simplified check}int main(int argc, char argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();}
Atminties valdymo supratimas be pagrindų
C++, atminties valdymas apima nustatymą, kuris ištrinti operatorių naudoti, kai objektas ištrinamas, ypač subklasifikavimo scenarijuose. Tokiais atvejais C++ naudoja dinaminio įvedimo koncepciją, kad nustatytų tikrąjį objekto tipą vykdymo metu. Tai būtina, nes kai pagrindinės klasės nuoroda nurodo išvestinės klasės objektą, turi būti iškviestas išvestinės klasės destruktorius ir trynimo operatorius.
Pateiktame pavyzdyje bazinė klasė X ir poklasis ArenaPaskirtaX sukurti savo versijas naujas ir ištrinti operatoriai. Kai objektas pašalinamas, C++ patikrina jo tipą naudodama vptr (virtualios rodyklės) technika. Destruktorius yra virtualus, garantuojantis, kad trynimo seka prasidės poklasiu ir iškviečia teisingą dinaminio objekto tipo trynimo operaciją. Šis metodas yra labai svarbus siekiant užkirsti kelią atminties nutekėjimui ir užtikrinti, kad poklasio skirti ištekliai būtų tinkamai išleisti.
Kitas svarbus šio elgesio aspektas yra tai, kad C++ tiesiogiai nesaugo naujas ir ištrinti operatoriai V lentelė. Vietoj to, vykdymo laikas naudoja naikintuvą, kad patikrintų, ar iškviestas atitinkamas trynimo operatorius. Be šio metodo, sunaikinus objektą naudojant bazinės klasės žymeklį, atmintis būtų nebaigta, o ištekliai liktų nevaldomi. Tai pabrėžia virtualių naikintuvų svarbą C++ paveldėjimo hierarchijose, ypač kai naudojamas pasirinktinis atminties paskirstymas.
Dažnai užduodami klausimai apie C++ atminties valdymą
- Koks yra tikslas virtual destructor C++?
- A virtual destructor užtikrina, kad pašalinus objektą per bazinės klasės žymeklį, iškviečiamas išvestinės klasės destruktorius. Tai leidžia tinkamai išvalyti išteklius.
- Ar delete operatorius bus išsaugotas VTable?
- Ne, delete operatorius nėra saugomas VTable. Naikintojas yra virtualus, užtikrinantis tinkamą delete operatorius pasirenkamas pagal objekto dinaminį tipą.
- Kaip C++ nustato, kuri delete operatoriui skambinti?
- C++ naudoja dinaminį spausdinimą per vptr (virtualią žymeklį), kad pasirinktumėte tinkamą delete operatorius pagal ištrinamo objekto tipą.
- Kodėl yra vptr svarbu ištrinant poklasį?
- The vptr nurodo VTable, kurioje yra virtualių funkcijų, pvz., destruktoriaus, adresai. Tai užtikrina, kad atitinkama versija delete vykdomas, kai poklasio objektas ištrinamas.
- Ar galiu nepaisyti abiejų operator new ir operator delete C++?
- Nepaisymas operator new ir operator delete bet kurioje klasėje leidžia keisti atminties paskirstymo ir atlaisvinimo būdą, kaip parodyta pavyzdyje su X ir ArenaAllocatedX.
Išvada:
Renkantis tinkamą ištrinti operatorius C++ reikalauja suprasti, kaip sąveikauja virtualūs naikintuvai ir dinaminiai tipai. Kai poklasis nepaiso atminties valdymo funkcijų, kompiliatorius garantuoja, kad objekto sunaikinimui naudojamas atitinkamas operatorius.
Šis metodas apsaugo nuo atminties nutekėjimo ir garantuoja, kad poklasiams būdingi ištekliai bus tinkamai išvalyti. Per pavyzdžius ir VTable tyrinėjimą kursas apšviečia šį esminį C++ paveldėjimo komponentą ir tai, kaip kalba tvarko atminties atskyrimą.
Šaltiniai ir nuorodos
- Turinys, susijęs su pasirinkimu ištrinti operatorių C++ buvo pagrįsta informacija, rasta oficialiame C++ informaciniai dokumentai .
- Kompiliatoriaus elgsena ir VTable generavimo informacija buvo ištirta naudojant išteklius, kuriuos pateikė GCC dokumentacija .
- Pavyzdinis kodas buvo išbandytas ir vizualizuotas naudojant Kompiliatoriaus naršyklė („Godbolt“) įrankis, imituojantis skirtingų kompiliatorių kompiliavimo veikimą realiuoju laiku.