Waarom kan de runtime-omgeving niet beslissen om delete toe te passen of [] te verwijderen in plaats van de programmeur?

Ik heb gelezen dat de operator delete [] nodig is omdat de runtime-omgeving geen informatie bevat over of het toegewezen blok een array van objecten is die destructor-oproepen vereisen of niet, maar het houdt wel informatie over waar in het geheugen het toegewezen blok is opgeslagen, en natuurlijk ook de grootte van het blok.
Het zou nog een extra beetje metadata kosten om te onthouden of destructors moeten worden opgeroepen of niet, dus waarom zou je dat niet gewoon doen?

Ik ben er vrij zeker van dat er een goede verklaring is, ik twijfel er niet aan, ik wil het alleen weten.

18
@trinithis: ik krijg uw voorbeeld, maar hoe zit het met int * p = new int [34] hoe weet verwijderen [] dat het (ten minste) 34 moet verwijderen? * sizeof (int)?
toegevoegd de auteur Petruza, de bron
@GeneBushuyev, u verwart de handtekening van de (mogelijk door de gebruiker geleverde) implementatie van de delete-operator en het aanroepen van de operator. Met de standaard kan de implementatie een optioneel argument voor de grootte aannemen, dat is ingesteld op de grootte van het verwijderde object, dat ofwel wordt afgeleid uit de statische type-informatie (vandaar de UB voor void * ) of doorgegeven door de dtor in de vtable voor polymorfe objecten.
toegevoegd de auteur Simon Richter, de bron
Voor een voorbeeld van een toewijzer die de grootte van het vrijgegeven blok vereist: AmigaOS FreeMem() .
toegevoegd de auteur Simon Richter, de bron
Sorry, deze bestaan ​​alleen voor verwijderen van lidoperator. IMO, dit is een fout in de specificatie.
toegevoegd de auteur Simon Richter, de bron
@Simon Richter - er is geen optioneel argument voor de grootte, zie 18.6 van de standaard voor de handtekeningen. UB heeft niets te maken met het argument void * , de standaard legt uit dat de compiler en runtime niet hoeven te worden gevalideerd of iets speciaals doen als de verwijderde aanwijzer geen geldig object is, gemaakt met de juiste nieuw of nieuw []
toegevoegd de auteur Gene Bushuyev, de bron
Deze hele thread is verknoeid door onduidelijke vragen en onnauwkeurige antwoorden. Mensen verwarren delete-expressie en operator-delete. En ik weet nog steeds niet wat het OP wil weten.
toegevoegd de auteur Gene Bushuyev, de bron
@ trinithis - beide operatoren verwijderen en verwijderen [] nemen een ongeldig * , zij hebben geen kennis van het type.
toegevoegd de auteur Gene Bushuyev, de bron
Vraagt ​​u waarom er verwijderen [] is in plaats van verwijderen om de toewijzing en vernietiging van de array af te handelen?
toegevoegd de auteur Gene Bushuyev, de bron
toegevoegd de auteur Thomas Eding, de bron
Tenzij de norm expliciet zegt dat de grootte van de toewijzing ergens wordt opgeslagen (ik betwijfel het), is het niet altijd nodig om de toegewezen grootte op te slaan. Neem het volgende voorbeeld: int * p = new int; De enige manier om dit op een gedefinieerde manier te verwijderen is om delete op een int * aan te roepen. Dus de grootte is impliciet bekend van het feit dat je weet dat het een int is. (Opmerking: het aanroepen van verwijderen op een void * is UB: stackoverflow.com/a/941959/239916).
toegevoegd de auteur Thomas Eding, de bron
Het is een kleine kost, maar dan is de winst voor het betalen ook klein.
toegevoegd de auteur Don Reba, de bron

4 antwoord

Ik denk dat de reden is dat C ++ je niet dwingt om iets te doen wat je niet wilt. Het zou extra metadata toevoegen en als iemand het niet zou gebruiken, zou die extra overhead hen worden opgedrongen, in tegenstelling tot de ontwerpdoelen van de C ++ -taal.

Wanneer u de beschreven mogelijkheid wilt, biedt C ++ een manier. Dit wordt std: vector genoemd en u zou bijna altijd de voorkeur moeten geven aan een ander soort container of een slimme aanwijzer over onbewerkte nieuwe en verwijderen .

10
toegevoegd
Waarschijnlijk ... hoewel je zou kunnen beweren dat array-semantiek in C al gebroken was, dus het gaat gewoon verder vanaf daar. Ik ben bang dat het weglaten van de grootte een voortijdige micro-optimalisatie was.
toegevoegd de auteur Matthieu M., de bron
Ik denk niet dat dit de vraag was.
toegevoegd de auteur Gene Bushuyev, de bron
Over std: vector ... niet helemaal omdat het dynamisch kan groeien. Een eenvoudigere container zou werken. Niettemin, dichtbij genoeg.
toegevoegd de auteur Thomas Eding, de bron

Het komt er in de eerste plaats op neer dat het taalontwerp niet te veel beperkingen aan de uitvoerders wil opleggen. Veel C ++ -runtimes gebruiken malloc() voor :: operator new() en gratis() (min of meer) voor :: operator delete() . Standaard malloc / gratis bieden niet de boekhouding die nodig is voor het opnemen van een aantal elementen en bieden geen mogelijkheid om de malloc 'd-grootte te bepalen op vrije tijd. Het toevoegen van een ander niveau van geheugenmanipulatie tussen nieuwe Foo en malloc voor elk afzonderlijk object is vanuit het oogpunt van C/C ++ een behoorlijk grote sprong in complexiteit/abstractie. Het toevoegen van deze overhead aan elk object zou onder andere enkele geheugenbeheerbenaderingen verknoeien die ontworpen zijn om te weten wat de grootte van objecten is.

4
toegevoegd

Met C ++ kunt u zo efficiënt mogelijk werken, dus als ze het aantal elementen in een blok moesten bijhouden dat slechts 4 extra bytes per blok zou zijn.

Dit kan voor veel mensen nuttig zijn, maar het voorkomt ook totale efficiëntie voor mensen die het niet erg vinden [] in te stellen.

Het lijkt op het verschil tussen C ++ en Java. Java kan veel sneller worden geprogrammeerd omdat je je nooit zorgen hoeft te maken over garbagecollection, maar C ++, indien correct geprogrammeerd, kan efficiënter zijn en minder geheugen gebruiken omdat het geen van die variabelen hoeft op te slaan en je kunt beslissen wanneer verwijder geheugenblokken.

4
toegevoegd
Ga nu naar een algemeen doelheap allocator en kijk hoeveel overhead het heeft voor elke toewijzing. Ik zou niet verrast zijn door cijfers zoals 16-64 bytes voor elke toewijzing, dus 4-8 bytes voor grootte zal niemand opvallen. En nogmaals, waarschijnlijk houden de meeste toewijzers toch rekening met toewijzingen.
toegevoegd de auteur Gene Bushuyev, de bron

Er zijn twee dingen die hier moeten worden opgelost.

Ten eerste: de aanname dat malloc de exacte grootte behoudt die u heeft gevraagd.

Niet echt. malloc geeft alleen om het bieden van een blok dat groot genoeg is. Hoewel het om redenen van efficiëntie waarschijnlijk niet veel zal opleveren, zal het waarschijnlijk toch een blok van een "standaard" formaat bevatten, bijvoorbeeld een 2 ^ n bytes-blok. Daarom is de werkelijke grootte (zoals in, het aantal werkelijk toegewezen objecten) effectief onbekend.

Ten tweede: het vereiste "extra bit"

Inderdaad, de informatie die voor een bepaald object nodig is om te weten of het deel uitmaakt van een array of niet, zou gewoon een extra bit zijn. Logisch.

Wat de implementatie betreft: waar zou je dat stukje eigenlijk zetten?

Het geheugen dat is toegewezen aan het object zelf moet waarschijnlijk niet worden aangeraakt, het object gebruikt het toch. Ja?

  • op een bepaald platform kan dit in de aanwijzer zelf worden bewaard (sommige platforms negeren een deel van de bits), maar dit is niet draagbaar
  • dus het zou extra opslagruimte vereisen, tenminste een byte, behalve dat met uitlijningsproblemen het wel 8 bytes zou kunnen bedragen.

Demonstration: (not convincing as noted by sth, see below)

// A plain array of doubles:
+-------+-------+-------
|   0   |   1   |   2   
+-------+-------+-------

// A tentative to stash our extra bit
+-------++-------++-------++
|   0   ||   1   ||   2   ||
+-------++-------++-------++

// A correction since we introduced alignment issues
// Note: double's aligment is most probably its own size
+-------+-------+-------+-------+-------+-------
|   0   |  bit  |   1   |  bit  |   2   |  bit
+-------+-------+-------+-------+-------+-------

Humpf!

Bewerken

Daarom moet u op de meeste platforms (waar het adres van belang is) elke aanwijzer "verlengen" en de omvang ervan verdubbelen (uitlijningsproblemen).

Is het acceptabel dat alle wijzers slechts twee keer zo groot zijn, zodat je dat extra beetje kunt opbergen? Voor de meeste mensen denk ik dat het zou zijn. Maar C ++ is niet ontworpen voor de meeste mensen, het is in de eerste plaats bedoeld voor mensen die geven om prestaties, snelheid of geheugen, en als zodanig is dit niet acceptabel.

EINDE EDIT

Dus wat is het juiste antwoord? Het juiste antwoord is dat het kostbaar is om informatie te herstellen die verloren is gegaan door het type systeem. Helaas.

3
toegevoegd
@GeneBushuyev: wat voor soort toewijzer? Als we bijvoorbeeld kijken naar jemalloc (figuur 4 en bijbehorende tekst) ), wordt de verspilde ruimte gemeten in termen van bit. Het is relatief eenvoudig om speciale gevallen kleine toewijzingen efficiënt te maken.
toegevoegd de auteur Matthieu M., de bron
@sth: waar, de demonstratie is gebrekkig ... en ik gaf zoveel liefde aan mijn ASCII-tekening :( Ik heb gecorrigeerd, bedankt voor de melding.
toegevoegd de auteur Matthieu M., de bron
U zou niet echt een bit moeten opslaan voor elk array-element, omdat het niet geldig is om delete een pointer naar een element "binnen" de array. Alleen voor het eerste element moet u kunnen bepalen of een hele array dit niet volgt.
toegevoegd de auteur sth, de bron
Nogmaals, hebt u de overhead van doelheaptoewijzers voor doelgroepen gecontroleerd?
toegevoegd de auteur Gene Bushuyev, de bron