newoperatory i delete

Język C++ obsługuje dynamiczną alokację i cofanie przydziału obiektów przy użyciu new operatorów i delete . Te operatory przydzielają pamięć dla obiektów z puli nazywanej bezpłatnym magazynem (nazywanym również stertą). Operator new wywołuje funkcję operator newspecjalną , a delete operator wywołuje funkcję operator deletespecjalną .

Aby uzyskać listę plików biblioteki w bibliotece środowiska uruchomieniowego języka C i standardowej biblioteki języka C++, zobacz Funkcje biblioteki CRT.

Operator new

Kompilator tłumaczy instrukcję, taką jak ta, na wywołanie funkcji operator new:

char *pch = new char[BUFFER_SIZE];

Jeśli żądanie dotyczy zera bajtów magazynu, operator new zwraca wskaźnik do odrębnego obiektu. Oznacza to, że powtarzające się operator new wywołania zwracają różne wskaźniki.

Jeśli żądanie alokacji nie ma wystarczającej ilości pamięci, operator new zgłasza wyjątek std::bad_alloc . Lub zwraca nullptr wartość , jeśli użyto formularza new(std::nothrow)umieszczania lub jeśli połączono cię z pomocą techniczną bez zgłaszaniaoperator new. Aby uzyskać więcej informacji, zobacz Zachowanie błędu alokacji.

Dwa zakresy funkcji operator new zostały opisane w poniższej tabeli.

Zakres funkcji operator new

Operator Zakres
::operator new Globalnie
nazwa-klasy::operator new Klasa

Pierwszym argumentem operator new musi być typ size_t, a zwracany typ to zawsze void*.

Funkcja globalna operator new jest wywoływana, gdy new operator jest używany do przydzielania obiektów wbudowanych typów, obiektów typu klasy, które nie zawierają funkcji zdefiniowanych przez operator new użytkownika i tablic dowolnego typu. new Gdy operator jest używany do przydzielania obiektów typu klasy, w którym operator new zdefiniowano klasę, wywoływana operator new jest ta klasa.

Funkcja zdefiniowana operator new dla klasy jest statyczną funkcją składową (która nie może być wirtualna), która ukrywa funkcję globalną operator new dla obiektów tego typu klasy. Rozważ przypadek użycia new do przydzielenia i ustawienia pamięci na daną wartość:

#include <malloc.h>
#include <memory.h>

class Blanks
{
public:
    Blanks(){}
    void *operator new( size_t stAllocateBlock, char chInit );
};
void *Blanks::operator new( size_t stAllocateBlock, char chInit )
{
    void *pvTemp = malloc( stAllocateBlock );
    if( pvTemp != 0 )
        memset( pvTemp, chInit, stAllocateBlock );
    return pvTemp;
}
// For discrete objects of type Blanks, the global operator new function
// is hidden. Therefore, the following code allocates an object of type
// Blanks and initializes it to 0xa5
int main()
{
   Blanks *a5 = new(0xa5) Blanks;
   return a5 != 0;
}

Argument podany w nawiasach new jest przekazywany jako Blanks::operator newchInit argument. Jednak funkcja globalna operator new jest ukryta, powodując kod, taki jak następujące polecenie, aby wygenerować błąd:

Blanks *SomeBlanks = new Blanks;

Kompilator obsługuje tablicę new składową i delete operatory w deklaracji klasy. Przykład:

class MyClass
{
public:
   void * operator new[] (size_t)
   {
      return 0;
   }
   void   operator delete[] (void*)
   {
   }
};

int main()
{
   MyClass *pMyClass = new MyClass[5];
   delete [] pMyClass;
}

Zachowanie niepowodzenia alokacji

Funkcja new w standardowej bibliotece języka C++ obsługuje zachowanie określone w standardzie C++ od języka C++98. Jeśli żądanie alokacji nie ma wystarczającej ilości pamięci, operator new zgłasza wyjątek std::bad_alloc .

Starszy kod C++ zwrócił wskaźnik o wartości null dla alokacji, która zakończyła się niepowodzeniem. Jeśli masz kod, który oczekuje nierzucanej wersji newprogramu , połącz program za pomocą nothrownew.objpolecenia . Plik nothrownew.obj zastępuje plik globalny operator new wersją, która zwraca wartość nullptr , jeśli alokacja zakończy się niepowodzeniem. operator new nie zgłasza std::bad_allocjuż . Aby uzyskać więcej informacji o nothrownew.obj plikach opcji konsolidatora i innych plikach opcji, zobacz Opcje łącza.

Nie można mieszać kodu sprawdzającego wyjątki z globalnego operator new kodu sprawdzającego wskaźniki o wartości null w tej samej aplikacji. Można jednak nadal tworzyć klasy lokalne operator new , które zachowują się inaczej. Taka możliwość oznacza, że kompilator musi domyślnie działać defensywnie i uwzględniać kontrole zwracania wskaźnika null w new wywołaniach. Aby uzyskać więcej informacji na temat sposobu optymalizacji tych testów kompilatora, zobacz /Zc:throwingnew.

Obsługa niewystarczającej ilości pamięci

Sposób testowania nieudanej alokacji z new wyrażenia zależy od tego, czy używasz standardowego mechanizmu wyjątków, czy też używasz zwrotu nullptr . Standard C++ oczekuje, że alokator zgłosi wyjątek std::bad_alloc lub klasę pochodzącą z std::bad_allocklasy . Taki wyjątek można obsłużyć, jak pokazano w tym przykładzie:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   try {
      int *pI = new int[BIG_NUMBER];
   }
   catch (bad_alloc& ex) {
      cout << "Caught bad_alloc: " << ex.what() << endl;
      return -1;
   }
}

W przypadku korzystania z nothrow formularza newmożna przetestować błąd alokacji, jak pokazano w tym przykładzie:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new(nothrow) int[BIG_NUMBER];
   if ( pI == nullptr ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

Możesz przetestować alokację pamięci, gdy użyto nothrownew.obj pliku do zastąpienia globalnego operator new , jak pokazano poniżej:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new int[BIG_NUMBER];
   if ( !pI ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

Można podać procedurę obsługi żądań alokacji pamięci, których nie można wykonać. Aby obsłużyć taką awarię, można napisać niestandardową procedurę odzyskiwania. Może to na przykład zwolnić część zarezerwowanej pamięci, a następnie zezwolić na ponowne uruchomienie alokacji. W celu uzyskania więcej informacji, zobacz następujący temat: _set_new_handler.

Operator delete

Pamięć, która jest dynamicznie przydzielana przy użyciu operatora, można zwolnić za pomocą newdelete operatora . Operator delete wywołuje operator delete funkcję, która zwalnia pamięć z powrotem do dostępnej puli. delete Użycie operatora powoduje również wywołanie destruktora klas (jeśli istnieje).

Istnieją funkcje globalne i o operator delete zakresie klas. Dla danej klasy można zdefiniować tylko jedną operator delete funkcję. Jeśli została zdefiniowana, ukrywa funkcję globalną operator delete . Funkcja globalna operator delete jest zawsze wywoływana dla tablic dowolnego typu.

Funkcja globalna operator delete . Istnieją dwie formy dla funkcji globalnych operator delete i składowych operator delete klasy:

void operator delete( void * );
void operator delete( void *, size_t );

Tylko jeden z poprzednich dwóch formularzy może być obecny dla danej klasy. Pierwszy formularz przyjmuje jeden argument typu void *, który zawiera wskaźnik do obiektu w celu cofnięcia przydziału. Drugi formularz, wielkość przydziału, przyjmuje dwa argumenty: pierwszy jest wskaźnikiem do bloku pamięci w celu cofnięcia przydziału, a drugi jest liczbą bajtów do cofnięcia przydziału. Zwracany typ obu formularzy to void (operator delete nie może zwrócić wartości).

Celem drugiego formularza jest przyspieszenie wyszukiwania odpowiedniej kategorii rozmiaru obiektu do usunięcia. Te informacje często nie są przechowywane w pobliżu samej alokacji i prawdopodobnie nie są one przechowywane w pamięci podręcznej. Drugi formularz jest przydatny, gdy operator delete funkcja z klasy bazowej jest używana do usuwania obiektu klasy pochodnej.

Funkcja operator delete jest statyczna, więc nie może być wirtualna. Funkcja operator delete przestrzega kontroli dostępu zgodnie z opisem w temacie Kontrola dostępu do składowych.

W poniższym przykładzie przedstawiono funkcje zdefiniowane przez operator new użytkownika i operator delete przeznaczone do rejestrowania alokacji i cofania pamięci:

#include <iostream>
using namespace std;

int fLogMemory = 0;      // Perform logging (0=no; nonzero=yes)?
int cBlocksAllocated = 0;  // Count of blocks allocated.

// User-defined operator new.
void *operator new( size_t stAllocateBlock ) {
   static int fInOpNew = 0;   // Guard flag.

   if ( fLogMemory && !fInOpNew ) {
      fInOpNew = 1;
      clog << "Memory block " << ++cBlocksAllocated
          << " allocated for " << stAllocateBlock
          << " bytes\n";
      fInOpNew = 0;
   }
   return malloc( stAllocateBlock );
}

// User-defined operator delete.
void operator delete( void *pvMem ) {
   static int fInOpDelete = 0;   // Guard flag.
   if ( fLogMemory && !fInOpDelete ) {
      fInOpDelete = 1;
      clog << "Memory block " << cBlocksAllocated--
          << " deallocated\n";
      fInOpDelete = 0;
   }

   free( pvMem );
}

int main( int argc, char *argv[] ) {
   fLogMemory = 1;   // Turn logging on
   if( argc > 1 )
      for( int i = 0; i < atoi( argv[1] ); ++i ) {
         char *pMem = new char[10];
         delete[] pMem;
      }
   fLogMemory = 0;  // Turn logging off.
   return cBlocksAllocated;
}

Powyższy kod może służyć do wykrywania "wycieku pamięci", czyli pamięci przydzielonej do bezpłatnego sklepu, ale nigdy nie została zwolniona. Aby wykryć przecieki, operatory globalne new i delete są definiowane ponownie w celu zliczenia alokacji i cofnięcia alokacji pamięci.

Kompilator obsługuje tablicę new składową i delete operatory w deklaracji klasy. Przykład:

// spec1_the_operator_delete_function2.cpp
// compile with: /c
class X  {
public:
   void * operator new[] (size_t) {
      return 0;
   }
   void operator delete[] (void*) {}
};

void f() {
   X *pX = new X[5];
   delete [] pX;
}