Kietas, statinis ir krūva C ++

Aš ieškojau, bet aš nesupratiau šių trijų dalykų. Kada turiu naudoti dinaminį paskirstymą (krūvoje) ir kokie yra jo tikri privalumai? Kokios yra statikos ir kamino problemos? Ar galiu parašyti visą programą be kintamųjų paskirstymo krūvos?

Girdėjau, kad kitomis kalbomis yra „šiukšlių surinkėjas“, todėl nereikia jaudintis dėl atminties. Ką daro šiukšlių surinkėjas?

Ką galėtumėte valdyti atmintyje, kad nenaudotumėte šio šiukšlių surinkėjo?

Kai tik kažkas man pasakė, kad su šia išraiška:

 int * asafe=new int; 

Turiu žymiklį į rodyklę. Ką tai reiškia? Tai skiriasi:

 asafe=new int; 

?

132
03 янв. Hai nustatytas sausio 03 d 2009-01-03 08:41 '09 8:41 val. 2009-01-03 08:41
@ 9 atsakymai

Panašus klausimas buvo užduotas , bet jis neklausė apie statinį.

Santrauka apie tai, kokia statinė atmintis, krūva ir kaminai:

  • Statinis kintamasis iš esmės yra pasaulinis kintamasis, net jei negalite prieiti prie jo visame pasaulyje. Paprastai jam yra adresas, kuris yra vykdomajame faile. Visai programai yra tik viena kopija. Nesvarbu, kiek kartų įvedate funkcijų skambutį (ar klasę) (ir kiek siūlų!), Šis kintamasis priklauso tai pačiai atminties vietai.

  • Krūva yra atminties krūva, kurią galima naudoti dinamiškai. Jei norite 4kb objekto, dinamiškas platintojas peržiūrės laisvos vietos sąrašą krūvoje, pasirinkite 4kb gabalą ir persiųs jį jums. Kaip taisyklė, dinaminis atminties paskirstiklis (malloc, naujas ir tt) vyksta atminties pabaigoje ir veikia priešinga kryptimi.

  • Paaiškinimas, kaip krūva auga ir mažėja, yra šiek tiek už šio atsakymo apimties, tačiau pakanka pasakyti, kad visada įtraukiate ir pašalinate tik nuo pabaigos. Stelažai paprastai prasideda aukštai ir auga iki žemesnių adresų. Jūs baigsite atminties, kai kaminai susiduria su dinamišku paskirstikliu kažkur viduryje (bet tai reiškia fizinę ir virtualią atmintį ir susiskaidymą). Kelioms siūloms reikės kelių kaminų (paprastai šis procesas pasilieka minimalų kamino dydį).

Jei norite naudoti kiekvieną iš jų:

  • Statika / globaliai yra naudingi atmintyje, kuri, kaip žinote, jums visada reikės, ir jūs žinote, kad jūs niekada nenorite išlaisvinti. (Beje, įterptą aplinką galima laikyti tik statine atmintimi ... kamino ir krūvos dalis yra žinomos adresų erdvės dalis, kurią dalijasi trečiojo tipo atmintis: programos kodas. Programos dažnai atlieka dinaminį paskirstymą iš savo statinės atminties, kai jiems reikalingi tokie dalykai kaip susieti sąrašai Bet nepriklausomai nuo to, ar pati statinė atmintis (buferis) pati nėra „paskirstyta“, ir kiti objektai yra priskirti iš šiam tikslui išsaugotos atminties, o ne įterpti, o konsoliniai žaidimai dažnai vengia įterptųjų dinamų. atminties mechanizmai, kuriais siekiama griežtai kontroliuoti platinimo procesą, naudojant visų dydžių buferius visiems paskirstymams.)

  • Kraujo kintamieji yra naudingi, kai žinote, kad tol, kol funkcija yra vienoje vietoje (kažkur ant kamino), jums reikės kintamųjų. Rinkiniai yra tinkami kintamiesiems, kurių reikia kodui, kur jie yra, bet kurių nereikia už šio kodo. Jie taip pat yra labai malonūs, kai pasiekiate šaltinį, pvz., Failą, ir norite, kad ištekliai automatiškai paliktų, kai paliksite šį kodą.

  • Heap paskirstymas (dinamiškai priskirta atmintis) yra naudinga, jei norite būti lankstesni nei aukščiau. Dažnai funkcija vadinama atsakyti į įvykį (vartotojas spustelėja mygtuką „Sukurti >

Šiukšlių surinkimas

Neseniai aš girdėjau daug apie tai, kaip puikūs šiukšlių rinkėjai, todėl galbūt būtų naudinga šiek tiek ypatingas balsas.

Šiukšlių surinkimas yra puikus mechanizmas, kai našumas nėra didelė problema. Girdėjau, kad GC vis geriau ir sunkiau, bet faktas yra tas, kad jums gali būti priversta priimti bausmę už vykdymą (priklausomai nuo naudojimo atvejo). Ir jei esate tingus, jis vis tiek gali veikti neteisingai. Geriausiu metu šiukšlių surinkėjai supranta, kad jūsų atmintis išnyksta, kai supranta, kad joje nėra daugiau nuorodų (žr. Nuorodų skaičių ). Bet jei turite objektą, kuris nurodo save (galbūt nurodydamas kitą grįžtamąjį objektą), tada atskaitos skaičiavimas savaime nereiškia, kad atmintis gali būti ištrinta. Tokiu atveju GC turėtų apžiūrėti visą referencinę sriubą ir sužinoti, ar yra kokių nors salų, kurias paminėtų patys. Neprisijungęs, norėčiau pasiūlyti, kad operacijai O (n ^ 2), bet kokia ji būtų, ji gali pablogėti, jei jus domina našumas. (Edit: Martin B rodo, kad tai yra O (n) pakankamai efektyviems algoritmams. Tai vis dar yra O (n) per daug, jei jus domina našumas ir galite nemokamai gauti neribotą laiką be šiukšlių surinkimo.)

Asmeniškai, išgirdęs, kad žmonės sako, kad „C ++“ neturi šiukšlių surinkimo, mano proto žymės yra C ++ funkcija, bet tikriausiai esu mažuma. Tikriausiai sunkiausias dalykas, su kuriuo žmonės gali sužinoti apie programavimą C ir C ++ programose, yra rodikliai ir teisingas jų dinaminių atminties paskirstymų tvarkymas. Kai kurios kitos kalbos, pvz., „Python“, be GC būtų baisios, taigi, manau, kad jis ateina į tai, ko norite iš kalbos. Jei jums reikia patikimo veikimo, C ++ be šiukšlių surinkimo yra vienintelis dalykas, kurį galiu galvoti. Jei norite naudoti paprastą naudojimą ir mokytis ratų (kad išgelbėtų jus nuo kritimo, nereikalaujant išmokti „teisingo“ atminties valdymo), pasirinkite kažką su GC. Net jei žinote, kaip gerai valdyti atmintį, tai padės sutaupyti laiko, kurį galite išleisti kitam kodui optimizuoti. Tiesą sakant, tai nėra tiek daug, bet jei jums tikrai reikia patikimo veikimo (ir gebėjimo tiksliai žinoti, kas atsitinka, kai po viršeliais), laikėsi C ++. Yra priežastis, kad kiekvienas didžiausias žaidimo variklis, kurį kada nors girdėjau, yra C + + (jei ne C ar surinkimas). Python ir kiti yra puikūs skriptui, bet ne pagrindiniam žaidimo varikliui.

187
03 янв. atsakymas pateikiamas rinkose 03 Jan. 2009-01-03 17:08 '09 at 17:08 2009-01-03 17:08

Žinoma, viskas nėra pakankamai tiksli. Perskaitykite jį su druska :)

Na, trys dalykai, į kuriuos kalbate, yra automatiniai, statiški ir dinamiški laikymo laikai, kurie turi kažką bendro su tuo, kiek ilgai objektai gyvena ir kada jie pradeda gyventi.


Automatinis laikymo laikas

Trumpiems ir mažiems duomenims, kuriems reikalingi tik vietiniai duomenys, naudojate automatinį saugojimo laiką:

 if(some condition) { int a[3]; // array a has automatic storage duration fill_it(a); print_it(a); } 

Gyvenimo laikas baigiasi, kai išeisime iš bloko, ir prasideda, kai tik nustatomas objektas. Jie yra paprasčiausias sandėliavimo būdas ir greičiau nei dinaminio saugojimo atveju.


Statinis laikymo laikas

Jūs naudojate statinių saugojimo laiką laisviems kintamiesiems, prie kurių bet koks kodas gali būti prieinamas visą laiką, jei jų plotas leidžia naudoti tokį naudojimą (vardų sritis) ir vietinius kintamuosius, kurie turi prailginti jų gyvenimą, kai jie palieka savo teritoriją (vietinį rajoną). ) ir narių kintamieji, kurie turėtų būti atskirti nuo visų jų klasės objektų (apimties klasės). Jų gyvavimo trukmė priklauso nuo tūrio, kuriame jie yra. Jie gali turėti vardų sritį ir vietinę zoną bei klasės zoną . Tai, kas pasakytina apie abu šiuos dalykus, yra tada, kai prasideda gyvenimas, gyvenimas baigiasi programos pabaigoje . Štai du pavyzdžiai:

 // static storage duration. in global namespace scope string globalA; int main() { foo(); foo(); } void foo() { // static storage duration. in local scope static string localA; localA += "ab" cout << localA; } 

Programa išspausdina ababab , nes išeinant iš bloko localA nėra sunaikinta. Galima sakyti, kad objektai su vietine teritorija prasideda, kai kontrolė pasiekia savo apibrėžimą . localA tai atsitinka, kai įvedamas funkcijų kūnas. Dėl vardų srities objektų gyvavimo trukmė prasideda nuo programos paleidimo . Tas pats pasakytina ir apie stacionarius taikymo srities objektus:

 class A { static string classScopeA; }; string A::classScopeA; A a, b;  ==  == > 

Kaip matote, classScopeA nėra susieta su konkrečiais jos klasės objektais, bet pačia klase. Visų pirmiau minėtų trijų pavadinimų adresas yra tas pats, ir visi nurodo tą patį objektą. Yra speciali taisyklė apie tai, kada ir kaip statiniai objektai inicijuojami, bet dėl ​​to negalime nerimauti. Tai reiškė statinį inicijavimo gedimą.


Dinaminis laikymo laikas

Paskutinis saugojimo laikas yra dinamiškas. Jį naudojate, jei norite, kad objektai būtų gyvi kitoje saloje, ir norite, kad aplink šią nuorodą būtų rodomos nuorodos. Juos taip pat naudojate, jei jūsų objektai yra dideli ir norite sukurti dydžio matricas, kurios žinomos tik vykdymo metu . Dėl šio lankstumo dinamiškos saugojimo trukmės objektai yra sudėtingi ir valdomi lėtai. Tokio dinaminio trukmės objektai prasideda, kai įvyksta atitinkamas naujas operatoriaus skambutis:

 int main() { // the object that s points to has dynamic storage // duration string *s = new string; // pass a pointer pointing to the object around. // the object itself isn't touched foo(s); delete s; } void foo(string *s) { cout << s->size(); } 

Jo trukmė baigiasi tik tada, kai skambinate ištrinti. Jei pamiršote, šie objektai niekada nesibaigia. Ir klasės objektai, apibrėžiantys deklaruotą naudotojo konstruktorių, jų destruktoriai nebus vadinami. Objektams, turintiems dinaminę atmintį, reikia rankiniu būdu apdoroti savo išteklius ir su jais susijusį atminties šaltinį. Yra bibliotekų, kurios palengvintų jų naudojimą. Tikslus tam tikrų objektų šiukšlių rinkimas gali būti nustatytas naudojant protingą žymiklį:

 int main() { shared_ptr<string> s(new string); foo(s); } void foo(shared_ptr<string> s) { cout << s->size(); } 

Jums nereikia nerimauti dėl skambinimo ištrinimo. Bendrasis ptr tai daro jums, jei paskutinis žymeklis, susijęs su objektu, netenka galios. Bendras ptr pats turi automatinį saugojimo laiką. Taigi, jo veikimo trukmė automatiškai kontroliuojama, leidžianti jai patikrinti, ar nurodytas dinamiškas objektas turi būti ištrintas jo destruktoriuje. Jei norite pagalbos dėl shared_ptr, žr. Išplėstiniai dokumentai: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm

47
03 янв. Atsakymas suteiktas Johannes Schaub - litb 03 Jan 2009-01-03 09:12 '09 9:12 val. 2009-01-03 09:12

Tai buvo atidžiai pasakyta kaip „trumpas atsakymas“:

  • statinis kintamasis (klasė)
    gyvenimo trukmė = programos vykdymo laikas (1)
    matomumas = nustatomas prieigos modifikatorių (privačių / saugomų / viešųjų)

  • statinis kintamasis (pasaulinė apimtis)
    gyvenimo trukmė = programos vykdymo laikas (1)
    visible = kompiliavimo vienetas sukurtas (2)

  • krūvos kintamasis
    gyvenimo trukmė = apibrėžta jūsų (nauja pašalinti)
    matomumas = nustatomas jūsų (nepriklausomai nuo rodyklės)

  • kamino kintamasis
    matomumas = nuo skelbimo iki taikymo srities
    gyvenimas = nuo skelbimo iki taikymo srities paskelbimo


(1) tiksliau: nuo inicijavimo iki kompiliavimo bloko inicializavimo (t. Y. C / C ++ failo). Kompiliavimo vienetų inicijavimo tvarka nėra apibrėžta standarte.

(2) Saugokitės: jei antraštėje sukuriate statinį kintamąjį, kiekvienas kompiliavimo blokas gauna savo kopiją.

30
06 янв. atsakymas duotas peterchen 06 Sau 2009-01-06 01:46 '09, 1:46 val. 2009-01-06 01:46

Esu tikras, kad vienas iš pedantų netrukus ras geriausią atsakymą, tačiau pagrindinis skirtumas yra greitis ir dydis.

Stack

Daug greičiau skirti. Tai daroma „O“ (1), nes ji išsiskiria nustatant kamino rėmelį, todėl ji beveik nemokama. Trūkumas yra tas, kad jei išeisite iš kamino vietos, gausite kaulą. Jūs galite koreguoti kamino dydį, tačiau su IIRC žaidimui turite ~ 2 MB. Be to, kai tik išeisite iš funkcijos, viskas kaminai bus išvalyta. Todėl gali būti problema, kad ją vėliau reikės nurodyti. (Nurodymai dėl pasirinktų objektų pateikimo sukelia klaidų.)

krūva

Daug lėčiau skiriama. Bet jūs turite žaisti su juo ir pažymėti.

Šiukšlių surinkėjas

Šiukšlių surinkėjas yra kodas, kuris veikia fone ir atlaisvina atmintį. Kai skiriate atminties krūvą, labai lengva jį pamiršti, vadinamą atminties nutekėjimu. Laikui bėgant, jūsų paraiškoje suvartojama atmintis auga ir auga, kol ji veiks. Šiukšlių surinkėjas periodiškai atlaisvina atmintį, kurios jums nereikia, o tai padeda pašalinti šią klaidų klasę. Žinoma, tai pasiekia kainą, nes šiukšlių surinkėjas sulėtina darbą.

5
03 янв. Chris Smith atsakymas, pateiktas sausio 03 d 2009-01-03 09:06 '09 9:06 val. 2009-01-03 09:06

Kokios yra statikos ir kamino problemos?

Problema, susijusi su „statiniu“ paskirstymu, yra tai, kad paskirstymas atliekamas kompiliavimo metu: jūs negalite naudoti jos paskirstyti tam tikrą kintamą duomenų kiekį, kurio skaičius nežinomas iki vykdymo laiko.

Problema, susijusi su krovimu, yra ta, kad paskirstymas sunaikinamas, kai tik grąžinamas paskirstymo atlikėjas.

Ar galėčiau parašyti visą programą be kintamųjų paskirstymo krūvos?

Galbūt, bet ne ne trivialus, normalus, didelis taikymas (bet vadinamosios „įmontuotos“ programos gali būti parašytos be krūvos, naudojant pakategorę C + +).

Ką daro šiukšlių surinkėjas?

Jis stebi jūsų duomenis („pažymėkite ir gręžkite žemyn“), kad nustatytumėte, kada jūsų paraiška nebesuteikia. Tai patogi programai, nes programai nereikia išleisti duomenų ... bet šiukšlių surinkėjas gali būti brangus.

Šiukšlių surinkėjai nėra paprastas C ++ programavimo funkcija.

Ką galėtumėte valdyti atmintyje, kad nenaudotumėte šio šiukšlių surinkėjo?

Išnagrinėti C ++ mechanizmus deterministinei atminties išleidimui:

  • „statinis“: niekada neišleistas
  • „kamino“: kai tik kintamasis „viršija“
  • „krūva“: kai rodyklė ištrinama (paraiška yra aiškiai išbraukta arba netiesiogiai ištrinta per tam tikrą ar kitą paprogramę)
3
03 янв. ChrisW atsakė 03 Jan 2009-01-03 09:12 '09 9:12 val. 2009-01-03 09:12

Ką daryti, jei jūsų programa iš anksto nežino, kiek atminties skiriama (todėl negalima naudoti kamino kintamųjų). Pavyzdžiui, susieti sąrašai, sąrašai gali augti, iš anksto nežinodami, koks yra jo dydis. Todėl, kai nežinote, kiek elementų bus įterpta, kuprinės pasirinkimas yra pagrįstas susietam sąrašui.

1
03 янв. atsakymas pateikiamas kal 03 jan. 2009-01-03 09:36 '09 at 9:36 AM 2009-01-03 09:36

Atminties priskyrimas kaminai (funkciniai kintamieji, vietiniai kintamieji) gali būti problemiškas, jei jūsų kaminai yra per giliai ir perpildysite turimą atmintį, kad sukurtumėte kamino. Krūva skirta objektams, kuriuos reikia pasiekti iš kelių sriegių arba per visą programos gyvavimo ciklą. Visą programą galite rašyti nenaudodami krūvos.

Jūs galite lengvai filtruoti atmintį be šiukšlių surinkimo, bet taip pat galite diktuoti, kada atlaisvinami objektai ir atmintis. Paleidžiant „GC“, susidūriau su „Java“ problemomis, ir turiu realiu laiku procesą, nes GC yra išskirtinis siūlas (nieko negalima pradėti). Todėl, jei našumas yra kritiškas, ir jūs galite garantuoti, kad nėra nutekėjusių objektų, naudojant GC nėra labai naudinga. Priešingu atveju, jūs tiesiog nekenčiate gyvenimo, kai jūsų programa naudoja atmintį, ir jums reikia sekti nuotėkio šaltinį.

1
03 янв. Robo Elsnerio atsakymas dėl sausio 03 d 2009-01-03 09:09 '09, 09:09 2009-01-03 09:09

Kai kuriais atvejais HA privalumas yra dirginimas kitose; Pasitikėjimas GC skatina ne apie tai galvoti. Teoriškai, jis laukia, kol bus „tuščiosios“ arba kol jis bus būtinas, kai jis pavagia pralaidumą ir sukelia atsakymą vėluojant.

Bet jums nereikia „nemanau apie tai“. Kaip ir visa kita daugiapakopėse programose, kai galite duoti, galite duoti. Pavyzdžiui, .Net galite paprašyti GC; tai darydami, vietoj rečiau vartojamų ilgesnių GC, galite pradėti trumpesnį, sutrumpintą GC paleidimą ir paskirstyti vėlavimą, susijusį su šiais paslaugų duomenimis.

Tačiau tai nulemia pagrindinį GC pritraukimą, kuris, atrodo, „nėra rekomenduojamas daug dėl to, nes jis yra automatinis“.

Jei pirmą kartą užprogramavote, kol GC tapo įprasta ir buvo malonus malloc / free ir new / delete, tada gali pasirodyti, kad GC yra šiek tiek erzina ir (arba) įtartinas (kaip galima Daugelis programų leidžia atsitiktinai vėluoti. Tačiau taikomosiose programose, kuriose atsitiktinis latentinis laikas yra mažiau priimtinas, bendra reakcija yra išvengti GC aplinkos ir judėti grynai nevaldomo kodo kryptimi (arba Dievo nedraudžiama, ilgas miršta menas, surinkimo kalba)

Čia turėjau vasaros studentą, stažuotoją, protingą vaikiną, kuris buvo atskirtas nuo GC; он был настолько похож на супертиорцию GC, что даже при программировании на неуправляемом C/С++ он отказался следовать модели malloc/free new/delete, потому что, цитируя, "вам не обязательно делать это на современном языке программирования". И ты знаешь? Для крошечных приложений с коротким ходом вы действительно можете уйти от этого, но не для длительных приложений, выполняющих действия.

1
ответ дан frediano 22 мая '13 в 16:06 2013-05-22 16:06