Kiedy należy użyć nowego słowa kluczowego w C++?

Używam C++ przez krótki czas i zastanawiałem się nad nowym słowem kluczowym. Po prostu, powinienem go używać, czy nie?

1) ze słowem kluczowym new ...

MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";

2) Bez nowego słowa kluczowego...

MyClass myClass;
myClass.MyField = "Hello world!";

Z punktu widzenia implementacji, nie wydają się tak różne (ale jestem pewien, że są)... Jednak moim podstawowym językiem jest C# i oczywiście pierwsza metoda jest tym, do czego jestem przyzwyczajony.

[[6]}trudność wydaje się być taka, że metoda 1 jest trudniejsze w użyciu z klasami std C++.

Jaką metodę powinienem zastosować?

Update 1:

Ostatnio użyłem new dla heapmemory (lub free store) dla dużej tablicy, która wychodziła poza zakres (tzn. była zwracana z funkcji). Tam, gdzie wcześniej używałem stosu, co powodowało, że połowa elementów była uszkodzona poza zakresem, przejście na użycie stosu zapewniło, że elementy były w takcie. Yay!

Update 2:

Mój znajomy powiedział mi niedawno, że istnieje prosta zasada użycia słowa kluczowego new; za każdym razem, gdy wpiszesz new, wpisz delete.

Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.

Pomaga to zapobiec wyciekom pamięci, ponieważ zawsze musisz gdzieś umieścić plik delete (np. gdy wyciąć i wkleić go do destruktora lub w inny sposób).

Author: Nick Bolton, 2009-03-17

11 answers

Metoda 1 (przy użyciu new)

  • przydziela pamięć dla obiektu na darmowy sklep (jest to często to samo, co sterta )
  • wymaga, aby jawnie delete Twój obiekt później. (Jeśli go nie usuniesz, możesz utworzyć wyciek pamięci) {]}
  • pamięć zostaje przydzielona do czasu delete. (np. możesz return obiekt, który utworzyłeś za pomocą new)
  • przykład w pytaniu będzie wyciek pamięć, chyba że wskaźnik jest deleted; i powinna być zawsze usunięta , niezależnie od ścieżki kontrolnej lub jeśli zostaną rzucone wyjątki.

Metoda 2 (bez użycia new)

  • alokuje pamięć dla obiektu na stosie (gdzie znajdują się wszystkie zmienne lokalne) ogólnie jest mniej pamięci dostępnej dla stosu; jeśli przydzielisz zbyt wiele obiektów, ryzykujesz przepełnienie stosu.
  • You won ' t need to delete it później.
  • pamięć nie jest już przydzielana, gdy wychodzi poza zakres. (tzn. nie powinieneś return wskazywać na obiekt na stosie)

Jeśli chodzi o to, którą z nich użyć; wybierasz metodę, która działa najlepiej dla Ciebie, biorąc pod uwagę powyższe ograniczenia.

Kilka łatwych przypadków:

  • jeśli nie chcesz martwić się wywołaniem delete, (i potencjałem wywołania wycieków pamięci ) nie powinieneś używać new.
  • jeśli chcesz zwrócić wskaźnik do obiektu z funkcji, musisz użyć new
 266
Author: Daniel LeCheminant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 06:12:38

Istnieje istotna różnica między nimi.

Wszystko, co nie jest przypisane do new, zachowuje się podobnie do typów wartości w C# (a ludzie często mówią, że te obiekty są przydzielane na stosie, co jest prawdopodobnie najczęstszym / oczywistym przypadkiem, ale nie zawsze jest prawdą. Dokładniej, obiekty przydzielane bez użycia new mają Automatyczny czas przechowywania Wszystko przydzielone za pomocą new jest przydzielane na stercie, a wskaźnik do niej jest zwracany, dokładnie tak, jak typy referencyjne w C#.

Wszystko przypisane do stosu musi mieć stały rozmiar, określony w czasie kompilacji (kompilator musi poprawnie ustawić wskaźnik stosu lub jeśli obiekt jest członkiem innej klasy, musi dostosować rozmiar tej drugiej klasy). Dlatego tablice w C# są typami referencyjnymi. Muszą być, ponieważ w przypadku typów referencyjnych możemy zdecydować w czasie wykonywania, o ile pamięci poprosić. I to samo dotyczy tutaj. Tylko tablice o stałej wielkości (rozmiar, który można określić w czas kompilacji) może być przypisany z automatycznym czasem przechowywania (na stosie). Tablice o dynamicznym rozmiarze muszą być przydzielane na stercie przez wywołanie new.

(i tam kończy się podobieństwo do C#)

Teraz wszystko przydzielone na stosie ma "Automatyczny" czas przechowywania (możesz zadeklarować zmienną jako auto, ale jest to domyślne, jeśli nie podano innego typu przechowywania, więc słowo kluczowe nie jest używane w praktyce, ale to jest miejsce, w którym się pojawia from)

Automatyczny czas przechowywania oznacza dokładnie to, jak to brzmi, czas trwania zmiennej jest obsługiwany automatycznie. Natomiast wszystko przydzielone na stercie musi zostać ręcznie usunięte przez Ciebie. Oto przykład:

void foo() {
  bar b;
  bar* b2 = new bar();
}

Funkcja ta tworzy trzy wartości warte rozważenia:

W linii 1 deklaruje zmienną b typu bar na stosie (Automatyczny czas trwania).

On line 2, it declares a bar pointer b2 on the stack (automatic czas trwania), i wywołuje new, przydzielając obiekt bar na stercie. (dynamiczny czas trwania)

Gdy funkcja powróci, nastąpi: Po pierwsze, {[9] } wychodzi poza zakres (kolejność zniszczenia jest zawsze odwrotna od kolejności budowy). Ale b2 jest tylko wskaźnikiem, więc nic się nie dzieje, pamięć, którą zajmuje, jest po prostu uwolniona. Co ważne, pamięć, na którą wskazuje (instancja bar na stercie) nie jest dotykana. Tylko wskaźnik jest wolny, ponieważ tylko wskaźnik miał Automatyczny czas trwania. Po drugie, b jest poza zasięgiem, więc ponieważ ma Automatyczny czas trwania, jego Destruktor jest wywoływany, a pamięć jest uwalniana.

I bar instancja na stercie? Pewnie nadal tam jest. Nikt nie pofatygował się go usunąć, więc wyciekła nam pamięć.

Z tego przykładu możemy zobaczyć, że wszystko, co ma Automatyczny czas trwania, ma zagwarantowane , że jego Destruktor zostanie wywołany, gdy wyjdzie poza zasięg. To przydatne. / Align = "left" / trwa tak długo, jak tego potrzebujemy i może być dynamicznie skalowany, jak w przypadku tablic. To też jest przydatne. Możemy tego użyć do zarządzania alokacją pamięci. Co jeśli Klasa Foo przydzieliła trochę pamięci na stercie w swoim konstruktorze i usunęła tę pamięć w swoim destruktorze. Wtedy moglibyśmy uzyskać najlepsze z obu światów, bezpieczne alokacje pamięci, które są gwarantowane, aby zostać uwolnione ponownie, ale bez ograniczeń zmuszania wszystkiego do bycia na stosie.

I to jest prawie dokładnie jak działa większość kodu C++. Spójrz na przykład na std::vector biblioteki standardowej. Zazwyczaj jest on przydzielany na stosie, ale może być dynamicznie skalowany i zmieniany. I robi to poprzez wewnętrzne przydzielanie pamięci na stercie w razie potrzeby. Użytkownik klasy nigdy tego nie widzi, więc nie ma szans na wyciek pamięci lub zapomnienie o wyczyszczeniu tego, co przydzieliłeś.

Zasada ta nazywa się RAII( pozyskiwanie zasobów jest inicjalizacją) i może być rozszerzona na dowolny zasób, który musi być / align = "left" / (gniazda sieciowe, pliki, połączenia z bazami danych, blokady synchronizacji). Wszystkie z nich można zdobyć w konstruktorze i uwolnić w destruktorze, więc masz gwarancję, że wszystkie zasoby, które zdobędziesz, zostaną ponownie uwolnione.

Ogólnie rzecz biorąc, nigdy nie używaj nowego / Usuń bezpośrednio z kodu wysokiego poziomu. Zawsze owijaj go w klasę, która może zarządzać pamięcią za Ciebie i która zapewni jej ponowne uwolnienie. (Tak, mogą być wyjątki od tej reguły. W konkretne, Inteligentne Wskaźniki wymagają bezpośredniego wywołania new i przekazania wskaźnika do jego konstruktora, który następnie przejmuje kontrolę i zapewnia prawidłowe wywołanie delete. Ale to wciąż bardzo ważna zasada)

 107
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-22 12:34:10

Jaką metodę powinienem zastosować?

To prawie nigdy nie zależy od preferencji typowania, ale od kontekstu. Jeśli chcesz zatrzymać obiekt w kilku stosach lub jeśli jest zbyt ciężki dla stosu, przydzielasz go w sklepie darmowym. Ponadto, ponieważ przydzielasz obiekt, jesteś również odpowiedzialny za zwolnienie pamięci. Wyszukiwanie operatora delete.

Aby złagodzić ciężar korzystania z zarządzania free-store ludzie wymyślili rzeczy takie jak auto_ptr i unique_ptr. I zdecydowanie polecam rzucić na nie okiem. Mogą nawet pomóc w twoich problemach z pisaniem; -)

 13
Author: dirkgently,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 16:53:23

Jeśli piszesz w C++, prawdopodobnie piszesz dla wydajności. Korzystanie z nowego i wolnego sklepu jest znacznie wolniejsze niż używanie stosu (szczególnie podczas korzystania z wątków), więc używaj go tylko wtedy, gdy go potrzebujesz.

Jak powiedzieli inni, potrzebujesz nowego, gdy twój obiekt musi żyć poza zakresem funkcji lub obiektu, obiekt jest naprawdę duży lub gdy nie znasz rozmiaru tablicy podczas kompilacji.

Staraj się także unikać używania delete. Zawiń swój nowy w inteligentny wskaźnik zamiast tego. Niech inteligentne wywołanie wskaźnika usunie za Ciebie.

Są przypadki, w których inteligentny wskaźnik nie jest inteligentny. Nigdy nie przechowuj std:: auto_ptr wewnątrz kontenera STL. Zbyt szybko usunie wskaźnik z powodu operacji kopiowania wewnątrz kontenera. Innym przypadkiem jest sytuacja, gdy masz naprawdę duży kontener wskaźników STL do obiektów. boost:: shared_ptr będzie miał mnóstwo prędkości nad głową, ponieważ uderza Referencja liczy się w górę iw dół. Lepiej w takim przypadku umieścić STL kontener do innego obiektu i nadać temu obiektowi Destruktor, który wywoła delete na każdym wskaźniku w kontenerze.

 7
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 02:22:26

Krótka odpowiedź brzmi: jeśli jesteś początkującym w C++, powinieneś nigdy używać new LUB delete. Zamiast tego powinieneś używać inteligentnych wskaźników, takich jak std::unique_ptr (lub rzadziej std::shared_ptr). W ten sposób nie musisz martwić się o wycieki pamięci. A nawet jeśli jesteś bardziej zaawansowany, najlepszą praktyką jest zazwyczaj zamknięcie niestandardowego sposobu, w jaki używasz new i delete w małą klasę (taką jak niestandardowy inteligentny wskaźnik), która jest dedykowana wyłącznie cyklowi życia obiektu problemy.

Oczywiście, za kulisami, te inteligentne wskaźniki nadal wykonują dynamiczną alokację i dealokację, więc kod z nich korzystający nadal będzie miał związany z tym narzut czasu pracy. Inne odpowiedzi omawiały te kwestie i jak podejmować decyzje projektowe, kiedy używać inteligentnych wskaźników, a nie tylko tworzyć obiekty na stosie lub włączać je jako bezpośrednie elementy obiektu, na tyle dobrze, że nie będę ich powtarzał. Ale moje streszczenie brzmiałoby: nie używaj mądrych wskaźniki lub dynamiczna alokacja, dopóki coś cię do tego nie zmusi.

 3
Author: Daniel Schepler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-05 18:09:34

Bez słowa kluczowego new przechowujesz to na stosie wywołań . Przechowywanie zbyt dużych zmiennych na stosie spowoduje przepełnienie stosu .

 2
Author: vartec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 16:51:13

Jeśli zmienna jest używana tylko w kontekście pojedynczej funkcji, lepiej jest użyć zmiennej stosu, np. opcji 2. Jak mówili inni, nie musisz zarządzać czasem życia zmiennych stosu - są one konstruowane i niszczone automatycznie. Ponadto przydzielanie / dealokacja zmiennej na stercie jest powolne w porównaniu. Jeśli twoja funkcja jest wywoływana wystarczająco często, zobaczysz ogromną poprawę wydajności, jeśli użyjesz zmiennych stosu w porównaniu ze zmiennymi stosu.

Że powiedziawszy, istnieje kilka oczywistych przypadków, w których zmienne stosu są niewystarczające.

Jeśli zmienna stosu ma duży rozmiar pamięci, istnieje ryzyko przepełnienia stosu. Domyślnie, rozmiar stosu każdego wątku wynosi 1 MB w systemie Windows. Jest mało prawdopodobne, że stworzysz zmienną stosu o rozmiarze 1 MB, ale musisz pamiętać, że wykorzystanie stosu kumuluje się. Jeśli twoja funkcja wywołuje funkcję, która wywołuje inną funkcję, która wywołuje inną funkcja, która..., zmienne stosu we wszystkich tych funkcjach zajmują miejsce na tym samym stosie. Funkcje rekurencyjne mogą szybko napotkać ten problem, w zależności od głębokości rekurencji. Jeśli jest to problem, można zwiększyć rozmiar stosu (niezalecane) lub przydzielić zmienną na stosie za pomocą nowego operatora (zalecane).

Innym, bardziej prawdopodobnym warunkiem jest to, że Twoja zmienna musi " żyć " poza zakresem twojej funkcji. W takim przypadku przydzieliłbyś zmienna na stercie tak, że może być osiągnięta poza zakresem danej funkcji.

 1
Author: Matt Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 17:09:28

Czy pomijasz myClass z funkcji, czy spodziewasz się, że istnieje poza tą funkcją? Jak niektórzy inni powiedzieli, chodzi o zakres, gdy nie przydzielasz na stertę. Kiedy opuszczasz funkcję, znika (w końcu). Jednym z klasycznych błędów popełnianych przez początkujących jest próba stworzenia lokalnego obiektu jakiejś klasy w funkcji i zwrócenia go bez przydzielania go na stertę. Pamiętam debugowanie tego typu rzeczy z moich wcześniejszych dni robiąc c++.

 1
Author: itsmatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 17:20:13

Prosta odpowiedź brzmi tak-new() tworzy obiekt na stercie (z niefortunnym skutkiem ubocznym, że musisz zarządzać jego żywotnością (jawnie wywołując delete), podczas gdy druga forma tworzy obiekt w stosie w bieżącym zakresie i ten obiekt zostanie zniszczony, gdy wyjdzie poza zakres.

 1
Author: Timo Geusch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 10:53:10

Krótka odpowiedź brzmi: tak, słowo kluczowe" nowy " jest niezwykle ważne, ponieważ gdy go używasz, dane obiektu są przechowywane na stercie, a nie na stosie, co jest najważniejsze!

 0
Author: RAGNO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 16:47:48

Druga metoda tworzy instancję na stosie, wraz z takimi rzeczami jak coś zadeklarowane int i lista parametrów, które są przekazywane do funkcji.

Pierwsza metoda robi miejsce dlawskaźnika na stosie, który ustawiłeś na miejsce w pamięci, gdzie na stercie - lub wolnym magazynie został przydzielony Nowy MyClass.

Pierwsza metoda wymaga również {[2] } tego, co tworzysz za pomocą new, podczas gdy w drugiej metodzie klasa jest automatycznie niszczony i zwalniany, gdy wypada poza zasięg (Zwykle następny nawias zamykający).

 0
Author: greyfade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-17 16:50:13