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:
- Alokujemy miejsce w pamięci: void *mem = malloc().
- Tworzymy obiekt w tej pamięci używając “Placement new”: Bubble *bubble = new (mem) Bubble().
- Pracujemy z naszym obiektem. Niczym nie różni się on od obiektów stworzonych normalnym new.
- Na koniec pracy jawnie wywołujemy destruktor(!) obiektu: bubble->~Bubble().
- 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!
March 8th, 2011 on 22:09
“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ń.”
To nie wskazuje zastosowania tego operatora.
March 9th, 2011 on 09:51
@anom
Chodzi mi o to, że operator placement new można wykorzystać do zwiększenia efektywności zarządzania pamięcią poprzez stworzenia własnego managera pamięci. Dzięki za feedback!
March 10th, 2011 on 12:35
bardziej chodziło mi tu o fakt, że niczym nie różni się to od przesuwania wskaźnika po takiej pamięci i jawnego wywoływania konstruktora. W zasadzie mamy wtedy nawet nieco krotszy zapis. Tak samo jak to ma miejsce w punkcie drugim. Wskazujemy wskaznikiem na miejsce w pamięci i przesuwamy go o sizeof(char).
Jak tak sie przyjzec temu wszystkiemu to jak by nie kombinowac to wychodzi na to, że mamy do czynienia ze zwykłą tablicą.
Ja oczekuje tu raczej jakiejś niezwykłej właściwości (nie wiem jakiej, zeby nie było że cie jakoś sprawdzam) która faktycznie ułatwi nam coś, skroci zapis, czy faktycznie przyspieszy daną operacje.
March 11th, 2011 on 09:36
Być może masz rację. Czy możesz wrzucić przykład, jak to realizujesz ?
March 11th, 2011 on 13:56
http://www.nopaste.pl/z7c
No a wskaznikiem jak wiadomo można skakać gdzie sie chce (w przykładzie po prostu przesuwam go na kolejny obiekt)
March 12th, 2011 on 05:19
Masz absolutnie rację. Tak mnie ten temat zainteresował, że zapytałem o różnice między tymi dwiema metodami na stackoverflow. Okazuje się, że konstrukcja *ptr = test(); powoduje 3 zdarzenia: najpierw tworzony jest obiekt tymczasowy na stosie, następnie przypisywany jest on do obiektu *ptr, na koniec obiekt ze stosu jest usuwany. Przyjmijmy, że Twoja klasa test w konstruktorze dynamicznie tworzy tablicę liczb: numbers = new int[100], a w destruktorze ją usuwa: delete[] numbers. Konsekwencje jawnego wywołania konstruktora mogą być następujące:
Operator placement new nie pociąga za sobą tych zagrożeń, ponieważ obiekt tworzony jest bezpośrednio we wskazanej pamięci (nie powstaje żaden obiekt tymczasowy na stosie, nie wywołuje się operator przypisania). Jest więc bezpieczniejszy w użyciu.
Oczywiście podany przez Ciebie przykład jest jak najbardziej poprawny – nie alokujesz dynamicznie żadnej pamięci, dlatego problem nie istnieje.
Link do mojego pytania na stackoverflow.com:
http://stackoverflow.com/questions/5279042/placement-new-vs-explicit-constructor-call-in-c
April 28th, 2011 on 19:32
>http://www.nopaste.pl/z7c
>*ptr = test(); //jawne wywolanie konstruktora
to nie jawne wywolanie konstruktora, tylko operatora= przy zalozeniu ze klasa test jest copy-constructible
co do placement new szefu dobrze pisze, przydaje sie glownie w allocatorach dla stla