12.7 Lokalizujący przydział pamięci

Za pomocą operatora new można przydzielić pamięc o z góry określonej lokalizacji. Istnieje do tego celu specjalna forma operatora new — dostępna po dołączeniu pliku nagłówkowego new — o następującej składni:

       new (adres) Typ;
       new (adres) Typ[wymiar];
W ten sposób przydzielamy na pojedynczy obiekt lub tablicę obszar pamięci rozpoczynający się od adresu będącego wartością wyrażenia adres. W zasadzie obszar ten powinien mieścić się w jakimś obszarze pamięci przydzielonym wcześniej za pomocą „zwykłego” new. Jest to operacja szybka, ponieważ tak naprawdę nie wymaga alokowania pamięci; kompilator zakłada, że programista wie co robi. Ponieważ tak naprawdę przydziału pamięci nie ma, obszar ten powinien zostać zwolniony przez wywołanie delete ze wskaźnikiem takiego typu i zawierającym ten adres, który był użyty w „prawdziwym” new, a nie w  new lokalizującym!

W poniższym przykładzie alokujemy () tablicę arr znaków (czyli bajtów) o wymiarze pozwalającym pomieścić tam trzy sz-elementowe tablice różnych typów(string, doubleint). Wewnątrz obszaru pamięci przez nią zajmowanego „alokujemy” trzy osobne tablice, obliczając za każdym razem (patrz na przykład ) pod jakim adresem powinny się rozpoczynać, aby pomieścić się w dostępnej pamięci:


P97: new.cpp     Lokalizujący przydział pamięci

      1.  #include <iostream>
      2.  #include <string>
      3.  
      4.  int main() {
      5.      using std::string; using std::cout; using std::endl;
      6.      int sz = 3;
      7.  
      8.      char* arr = new char[sz*(sizeof(string) +       
      9.                               sizeof(double)+sizeof(int))];
     10.  
     11.      string* nam = new (arr) string[sz]{"Sue", "Kim", "Joe"};
     12.      double* wei = new (arr+sz*sizeof(string))       
     13.                              double[sz]{55.5, 61.2, 81.5};
     14.      int* hei = new (arr+sz*(sizeof(string)+sizeof(double)))
     15.                              int[sz]{170, 165, 183};
     16.      for (int i = 0; i < sz; ++i)
     17.          cout << nam[i] << " " << wei[i] << " "
     18.               << hei[i] << endl;
     19.      delete [] arr;                                  
     20.  }

Wydruk

    Sue 55.5 170
    Kim 61.2 165
    Joe 81.5 183
świadczy o tym, że tablice zostały prawidłowo zaalokowane i zainicjowane. Pamiętamy oczywiście, aby zwalniając pamięć () użyć adresu otrzymanego przez „prawdziwe” new!

Opisany rodzaj przydzielania pamięci stosuje się też często aby wykorzystać na nowe dane wcześniej zaalokowane, a zawierające dane już niepotrzebne, obszary pamięci — jest to znacznie szybsze niż zwalnianie i potem przydzielanie pamięci od nowa.

T.R. Werner, 23 lutego 2022; 19:40