Szukaj…


Składnia

  • : :( opt ) new ( lista-wyrażeń ) ( opt ) new-type-id new-initializer ( opt )
  • : :( opt ) new ( lista-wyrażeń ) ( opt ) ( type-id ) new-initializer ( opt )
  • : :( opt ) usuń wyrażenie cast
  • : :( opt ) delete [] cast-expression
  • std :: unikalny_ptr < identyfikator-typu > nazwa_wariatu (nowy identyfikator-typu ( opt )); // C ++ 11
  • std :: shared_ptr < typ-id > nazwa_wariatu (nowy typ-id ( opt )); // C ++ 11
  • std :: shared_ptr < typ-id > nazwa_wariatu = std :: make_shared < typ-id > ( opt ); // C ++ 11
  • std :: unikalny_ptr < typ-id > nazwa_wariatu = std :: make_unique < typ-id > ( opt ); // C ++ 14

Uwagi

Wiodące :: zmusza operatora nowego lub delete do wyszukiwania w zakresie globalnym, zastępując przeciążone specyficzne dla klasy nowe lub operatora usuwania.

Opcjonalne argumenty występujące po new słowie kluczowym są zwykle używane do wywołania umieszczenia nowego , ale mogą również służyć do przekazywania dodatkowych informacji do alokatora, takich jak znacznik żądający alokacji pamięci z wybranej puli.

Przydzielony typ jest zwykle jawnie określony, np. new Foo , ale można go również zapisać jako auto (od C ++ 11) lub jako decltype(auto) (od C ++ 14), aby wydedukować go z inicjalizatora.

Inicjalizacja przydzielonego obiektu odbywa się zgodnie z tymi samymi regułami, co inicjalizacja zmiennych lokalnych. W szczególności obiekt zostanie zainicjowany domyślnie, jeśli inicjator nie zostanie pominięty, a podczas dynamicznego przydzielania typu skalarnego lub tablicy typu skalarnego nie ma gwarancji, że pamięć zostanie wyzerowana.

Obiekt tablicy utworzony przez nowe wyrażenie musi zostać zniszczony przy użyciu delete[] , niezależnie od tego, czy nowe wyrażenie zostało napisane za pomocą [] czy nie. Na przykład:

using IA = int[4];
int* pIA = new IA;
delete[] pIA;  // right
// delete pIA;  // wrong

Stos

Stos jest małym obszarem pamięci, w którym tymczasowe wartości są umieszczane podczas wykonywania. Przydzielanie danych do stosu jest bardzo szybkie w porównaniu do przydzielania sterty, ponieważ cała pamięć została już w tym celu przypisana.

int main() {
    int a = 0; //Stored on the stack
    return a;
}

Stos jest nazywany, ponieważ łańcuchy wywołań funkcji będą miały „tymczasowo” ułożone w stosy pamięci tymczasowej, przy czym każda z nich będzie korzystać z oddzielnej małej części pamięci.

float bar() {
    //f will be placed on the stack after anything else
    float f = 2;
    return f;
}

double foo() {
    //d will be placed just after anything within main()
    double d = bar();
    return d;
}

int main() {
    //The stack has no user variables stored in it until foo() is called
    return (int)foo();
}

Dane przechowywane na stosie są ważne tylko tak długo, jak długo zakres przydzielający zmienną jest nadal aktywny.

int* pA = nullptr;

void foo() {
    int b = *pA;
    pA = &b;
}

int main() {
    int a = 5;
    pA = &a;
    foo();
    //Undefined behavior, the value pointed to by pA is no longer in scope
    a = *pA;
}

Bezpłatne miejsce do przechowywania (sterty, alokacja dynamiczna ...)

Termin „sterta” jest ogólnym terminem obliczeniowym, oznaczającym obszar pamięci, z którego części można alokować i zwalniać niezależnie od pamięci zapewnianej przez stos .

W C++ Standard określa ten obszar jako Free Store, który jest uważany za dokładniejszy termin.

Obszary pamięci przydzielone z Free Store mogą żyć dłużej niż pierwotny zakres, w którym zostały przydzielone. Dane zbyt duże, aby można je było zapisać na stosie, można również przydzielić z Free Store .

Surowa pamięć może być przydzielana i zwalniana przez nowe i usuwane słowa kluczowe.

float *foo = nullptr;
{
    *foo = new float; // Allocates memory for a float
    float bar;              // Stack allocated 
} // End lifetime of bar, while foo still alive

delete foo;          // Deletes the memory for the float at pF, invalidating the pointer
foo = nullptr;       // Setting the pointer to nullptr after delete is often considered good practice

Możliwe jest również przydzielanie tablic o stałym rozmiarze z nowymi i usuwanymi , z nieco inną składnią. Alokacja tablic nie jest kompatybilna z alokacją macierzy, a ich mieszanie doprowadzi do uszkodzenia sterty. Przydzielenie tablicy alokuje również pamięć do śledzenia rozmiaru tablicy do późniejszego usunięcia w sposób zdefiniowany w implementacji.

// Allocates memory for an array of 256 ints
int *foo = new int[256];
// Deletes an array of 256 ints at foo
delete[] foo;

Podczas korzystania z new i delete zamiast malloc i free , konstruktor i destruktor zostaną wykonane (podobnie do obiektów opartych na stosie). To dlatego nowe i usuwane są preferowane zamiast malloc i bezpłatne .

struct ComplexType {
    int a = 0;

    ComplexType() { std::cout << "Ctor" << std::endl; }
    ~ComplexType() { std::cout << "Dtor" << std::endl; }
};

// Allocates memory for a ComplexType, and calls its constructor
ComplexType *foo = new ComplexType();
//Calls the destructor for ComplexType() and deletes memory for a Complextype at pC
delete foo;
C ++ 11

Począwszy od C ++ 11 zaleca się stosowanie inteligentnych wskaźników do wskazywania własności.

C ++ 14

C ++ 14 dodał std::make_unique do STL, zmieniając zalecenie, aby faworyzować std::make_unique lub std::make_shared zamiast używania „ new” i „ delete” .

Umieszczenie nowe

Są sytuacje, w których nie chcemy polegać na Free Store przy przydzielaniu pamięci i chcemy używać niestandardowych przydziałów pamięci przy użyciu new .

W takich sytuacjach możemy użyć funkcji Placement New , w której możemy powiedzieć operatorowi „nowy”, aby przydzielił pamięć z wcześniej przydzielonego miejsca w pamięci

Na przykład

int a4byteInteger;

char *a4byteChar = new (&a4byteInteger) char[4];

W tym przykładzie pamięć wskazywana przez a4byteChar to 4 bajty przydzielone do „stosu” za pomocą zmiennej całkowitej a4byteInteger .

Zaletą tego rodzaju alokacji pamięci jest fakt, że programiści kontrolują alokację. W powyższym przykładzie, ponieważ a4byteInteger jest alokowane na stosie, nie musimy wykonywać jawnego wywołania, aby „usunąć a4byteChar”.

Takie samo zachowanie można również osiągnąć w przypadku dynamicznej pamięci przydzielonej. Na przykład

int *a8byteDynamicInteger = new int[2];

char *a8byteChar = new (a8byteDynamicInteger) char[8];

W tym przypadku wskaźnik pamięci a8byteChar będzie odnosił się do pamięci dynamicznej przydzielonej przez a8byteDynamicInteger . W takim przypadku musimy jednak jawnie wywołać delete a8byteDynamicInteger aby zwolnić pamięć

Kolejny przykład dla klasy C ++

struct ComplexType {
    int a;

    ComplexType() : a(0) {}
    ~ComplexType() {}
};

int main() {
    char* dynArray = new char[256];

    //Calls ComplexType's constructor to initialize memory as a ComplexType
    new((void*)dynArray) ComplexType();

    //Clean up memory once we're done
    reinterpret_cast<ComplexType*>(dynArray)->~ComplexType();
    delete[] dynArray;

    //Stack memory can also be used with placement new
    alignas(ComplexType) char localArray[256]; //alignas() available since C++11

    new((void*)localArray) ComplexType();

    //Only need to call the destructor for stack memory
    reinterpret_cast<ComplexType*>(localArray)->~ComplexType();

    return 0;
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow