Kan plaatsing nieuw voor arrays worden gebruikt in een draagbare manier?

stemmen
31

Is het mogelijk om daadwerkelijk gebruik maken van de plaatsing nieuw te maken in draagbare code bij het gebruik ervan voor arrays?

Het lijkt erop dat de aanwijzer je terug van nieuwe te krijgen [] is niet altijd hetzelfde als het adres dat u pas in (5.3.4, toelichting 12 in de standaard lijkt te bevestigen dat dit juist is), maar ik zie niet in hoe je een buffer toe te wijzen voor de array te gaan of dit het geval is.

Het volgende voorbeeld toont het probleem. Gecompileerd met Visual Studio, dit voorbeeld resulteert in het geheugen van corruptie:

#include <new>
#include <stdio.h>

class A
{
    public:

    A() : data(0) {}
    virtual ~A() {}
    int data;
};

int main()
{
    const int NUMELEMENTS=20;

    char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
    A *pA = new(pBuffer) A[NUMELEMENTS];

    // With VC++, pA will be four bytes higher than pBuffer
    printf(Buffer address: %x, Array address: %x\n, pBuffer, pA);

    // Debug runtime will assert here due to heap corruption
    delete[] pBuffer;

    return 0;
}

Kijkend naar het geheugen, de compiler lijkt te worden met behulp van de eerste vier bytes van de buffer om een telling van het aantal items in te slaan. Dit betekent dat vanwege de buffer alleen sizeof(A)*NUMELEMENTSgroot, het laatste element in de array in toegewezen heap wordt geschreven.

Dus de vraag is kun je erachter komen hoeveel extra overhead uw implementatie wil om veilig te kunnen gebruiken plaatsing nieuwe []? In het ideale geval, ik heb een techniek die is draagbaar tussen verschillende compilers. Merk op dat, althans in het geval VC's, de overhead lijkt te verschillen voor verschillende klassen. Bijvoorbeeld, als ik de virtuele destructor verwijderen in het voorbeeld, het adres terug van nieuwe [] is gelijk aan het adres I passeren.

De vraag is gesteld op 18/08/2008 om 20:33
user
In andere talen...                            


7 antwoorden

stemmen
1

Ik denk dat gcc doet hetzelfde als MSVC, maar natuurlijk dit maakt het niet "portable" te maken.

Ik denk dat je het probleem te omzeilen wanneer numElements is inderdaad een compilatie constant, als volgt:

typedef A Arr[NUMELEMENTS];

A* p = new (buffer) Arr;

Dit moet de scalaire plaatsing nieuwe gebruiken.

antwoordde op 18/08/2008 om 20:45
bron van user

stemmen
23

Persoonlijk zou ik gaan met de mogelijkheid van het niet gebruiken plaatsing nieuw op de array en in plaats daarvan gebruik maken van de plaatsing van nieuwe op elk item in de array afzonderlijk. Bijvoorbeeld:

int main(int argc, char* argv[])
{
  const int NUMELEMENTS=20;

  char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
  A *pA = (A*)pBuffer;

  for(int i = 0; i < NUMELEMENTS; ++i)
  {
    pA[i] = new (pA + i) A();
  }

  printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);

  // dont forget to destroy!
  for(int i = 0; i < NUMELEMENTS; ++i)
  {
    pA[i].~A();
  }    

  delete[] pBuffer;

  return 0;
}

Ongeacht de methode die u gebruikt, zorg ervoor dat elk van deze items in de array u handmatig te vernietigen voordat je pBuffer verwijderen, zoals je zou kunnen eindigen met lekken;)

Let op : Ik heb dit niet gecompileerd, maar ik denk dat het moet (Ik ben op een machine die niet beschikt over een C ++ compiler geïnstalleerd) werken. Het geeft nog steeds de punt :) Hoop dat het helpt op een bepaalde manier!


Bewerk:

De reden dat het nodig heeft om bij te houden van het aantal elementen te houden is, zodat het kan doorlopen hen wanneer u belt verwijderen op de array en zorg ervoor dat de destructors worden opgeroepen op elk van de objecten. Als het niet weet hoeveel het er zijn zou het niet in staat zijn om dit te doen.

antwoordde op 18/08/2008 om 21:53
bron van user

stemmen
1

Vergelijkbaar met hoe je een enkel element zou gebruiken om de grootte te berekenen voor één plaatsing-nieuw, maken gebruik van een reeks van die elementen aan die nodig zijn voor een array size berekenen.

Als u de grootte voor andere berekeningen waarbij het aantal elementen niet bekend kan worden moet u kunt sizeof gebruiken (A [1]) en vermenigvuldig dat met de gewenste element tellen.

bv

char *pBuffer = new char[ sizeof(A[NUMELEMENTS]) ];
A *pA = (A*)pBuffer;

for(int i = 0; i < NUMELEMENTS; ++i)
{
    pA[i] = new (pA + i) A();
}
antwoordde op 18/08/2008 om 22:26
bron van user

stemmen
2

Bedankt voor de reacties. Met behulp van de plaatsing van nieuwe voor elk item in de array werd de oplossing die ik belandde via toen ik liep in deze (sorry, moeten vermelden dat in de vraag). Ik voelde gewoon dat er iets wat ik miste aan te doen met de plaatsing van nieuwe [] moet zijn geweest. Zoals het is, het lijkt alsof de plaatsing van nieuwe [] is in wezen onbruikbaar dankzij de standaard zodat de compiler om een ​​extra niet-gespecificeerde overhead toe te voegen aan de array. Ik zie niet in hoe je ooit zou kunnen gebruik maken van het veilig en draagbaar.

Ik ben niet eens echt duidelijk waarom het nodig de aanvullende gegevens, zoals je zou het niet noemen verwijderen [] op de array toch, dus ik weet niet helemaal waarom het nodig heeft om te weten hoeveel items zijn in het.

antwoordde op 18/08/2008 om 23:03
bron van user

stemmen
2

@James

Ik ben niet eens echt duidelijk waarom het nodig de aanvullende gegevens, zoals je zou het niet noemen verwijderen [] op de array toch, dus ik weet niet helemaal waarom het nodig heeft om te weten hoeveel items zijn in het.

Na het geven van dit enig nadenken, ik ben het met u. Er is geen reden waarom de plaatsing van nieuwe nodig zouden hebben om het aantal elementen op te slaan, want er is geen plaatsing verwijderd. Aangezien er geen plaatsing verwijderd, is er geen reden voor de plaatsing van nieuwe aan het aantal elementen op te slaan.

Ik heb ook dit getest met GCC op mijn Mac, met behulp van een klasse met een destructor. Op mijn systeem, was de plaatsing van nieuwe niet wijzigen van de cursor. Dit maakt me af of dit een VC ++ kwestie, en of dit kan de standaard in strijd (de norm niet specifiek ingegaan op deze, voor zover ik kan vinden).

antwoordde op 19/08/2008 om 01:14
bron van user

stemmen
4

@Derek

5.3.4, hoofdstuk 12 vertelt over de array toewijzing overhead en, tenzij ik het verkeerd lezen, lijkt te suggereren mij dat het is geldig voor de compiler om het op de plaatsing van nieuwe eveneens toe te voegen:

Deze lucht kan worden toegepast in alle nieuwe matrix-uitdrukkingen, waaronder die verwijzen naar de bibliotheekfunctie operator new [] (std :: size_t, void *) en andere plaatsen allocatie functies. De hoeveelheid overhead kan variëren van een aanroeping van de nieuwe naar de andere.

Dat gezegd hebbende, ik denk dat VC was het enige compiler die me problemen gaf met deze, uit het, GCC, CodeWarrior en ProDG. Ik zou moeten opnieuw controleren om er zeker van, dat wel.

antwoordde op 19/08/2008 om 09:16
bron van user

stemmen
2

Plaatsing nieuwe zelf is draagbaar, maar de aannames die je maakt over wat het doet met een gespecificeerde blok van het geheugen zijn niet draagbaar. Net als wat eerder werd gezegd, als je een compiler waren en kregen een stuk van het geheugen, hoe zou je weet hoe je een array toe te wijzen en elk element op de juiste vernietigen als alles wat je had was een pointer? (Zie de interface van de operator te verwijderen [].)

Bewerk:

En is er eigenlijk een plaatsing verwijderd, alleen het wordt alleen opgeroepen wanneer een constructeur gooit een uitzondering, terwijl de toewijzing van een array met de plaatsing van nieuwe [].

Of het nieuwe [] daadwerkelijk nodig heeft om bij te houden van het aantal elementen te houden of andere manier is iets dat aan de norm, die laat aan de compiler wordt overgelaten. Helaas, in dit geval.

antwoordde op 19/08/2008 om 20:36
bron van user

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more