Hey. Na pewno znasz operator new, który najpierw alokuje pamięć dla obiektu, a następnie uruchamia jego konstruktor. Mam dla Ciebie ciekawostkę – możesz operatorowi new nakazać, gdzie w pamięci ma utworzyć ten obiekt.

Jak tego używać

Cała procedura wygląda tak:

  1. Alokujemy miejsce w pamięci:  void *mem = malloc().
  2. Tworzymy obiekt w tej pamięci używając “Placement new”:  Bubble *bubble = new (mem) Bubble().
  3. Pracujemy z naszym obiektem. Niczym nie różni się on od obiektów stworzonych normalnym new.
  4. Na koniec pracy jawnie wywołujemy destruktor(!) obiektu:  bubble->~Bubble().
  5. I zwalniamy pamięć:  free(mem).

Zwróć uwagę na to, że po skończonej pracy destruktor obiektu należy wywołać jawnie, a pamięć ręcznie zwolnić. Nie istnieje żaden “placement delete”, który zrobi to za nas!

Przykład użycia

// przykładowa klasa "Bąbel" - taki sobie lekki obiekt ;)
class Bubble
{
public:
    Bubble()
    {
            cout << "konstruktor\n";
    }

    ~Bubble()
    {
            cout << "destruktor\n";
    }
};

int main()
{
    // 1. alokujemy pamięć dla obiektu
    void *mem = malloc(sizeof(Bubble));        

    // 2. tworzymy obiekt pod wskazanym adresem
    Bubble *bubble = new(mem) Bubble();

    //
    // 3. tutaj korzystamy sobie z obiektu "bubble"
    //

    // 4. na koniec - jawnie wywołujemy destruktor
    bubble->~Bubble();

    // 5. i zwalniamy pamięć
    free(mem);

    return 0;
}

Do czego to służy

Konstrukcja Placement new pozwala nam tworzyć obiekty w wybranym miejscu w pamięci. Dzięki temu:

  • mamy pewność, że nie zwróci NULL. Ponieważ pamięć alokujemy sobie z wyprzedzeniem – wiemy, że w krytycznym momencie  jej nie zabraknie,
  • pozwala na mapowanie pamięci. Niektóre urządzenia niskiego poziomu widziane są w systemie jako zwykły fragment pamięci. Tak było np. z kartami graficznymi – bufor ramki zaczynał się pod z góry znanym adresem, i miał rozmiar 320×240 bajtów (dla takiegoż trybu video). Wystarczyło więc stworzyć sobie tablicę typu unsigned char pod tym adresem, by pisać bezpośrednio do bufora ramki (czyli prosto na ekran)
  • umożliwia tworzenie pul pamięci. Gdy często tworzymy wiele małych obiektów, korzystanie ze zwykłego operatora new może wprowadzić duże opóźnienia związane z działaniem systemowego managera pamięci. Zamiast tego, na samym początku alokujemy sobie jeden duży obszar pamięci, w którym następnie tworzymy obiekty z wykorzystaniem “placement new”, już bez zbędnych opóźnień.

I to tyle z mojej strony. Jeśli masz własną propozycję zastosowania placement new – podziel się w komentarzu!

Related Posts: