W jakich przypadkach używam malloc i / lub nowego?

479

Widzę w C ++, że istnieje wiele sposobów przydzielania i bezpłatnego przesyłania danych. Rozumiem, że kiedy dzwonisz malloc, powinieneś zadzwonić, freea kiedy używasz newoperatora, powinieneś się sparować deletei błędem jest połączenie tych dwóch (np. Dzwonienie free()na coś, co zostało stworzone z newoperatorem), ale nie jestem pewien, kiedy powinienem używać malloc/ freea kiedy powinienem new/ deletew moich prawdziwych programach.

Jeśli jesteś ekspertem w C ++, daj mi znać wszelkie zasady praktyczne i konwencje, które stosujesz w tym zakresie.

JVApen
źródło
33
Chciałbym tylko dodać przypomnienie, że nie można mieszać dwóch stylów - to znaczy, że nie można użyć nowego do utworzenia obiektu, a następnie wywołać na nim free (), ani próbować usunąć bloku przydzielonego przez malloc (). Prawdopodobnie to oczywiste, ale mimo to ...
nsayer
32
Dobre odpowiedzi, wszystko, co muszę dodać (czego nie widziałem) to to, że new / delete wywołuje dla ciebie konstruktor / destruktor, a malloc / free nie. Tylko różnica, o której warto wspomnieć.
Bill K,
W nowoczesnym C ++ wciąż próbuję znaleźć powód, aby użyć jednego z nich.
Rahly,
Lub użyj żadnego z nich i przejdź do std: shared_ptr <T>. Wtedy nie musisz wcale usuwać.
Vincent

Odpowiedzi:

387

O ile nie jesteś zmuszony do używania C, nigdy nie powinieneś używaćmalloc . Zawsze używaj new.

Jeśli potrzebujesz dużej ilości danych, po prostu zrób coś takiego:

char *pBuffer = new char[1024];

Uważaj, ale to nie jest poprawne:

//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;

Zamiast tego powinieneś to zrobić usuwając tablicę danych:

//This deletes all items in the array
delete[] pBuffer;

Słowo newkluczowe jest sposobem na zrobienie tego w C ++ i zapewni, że Twój typ będzie miał wywołany konstruktor . Słowo newkluczowe jest również bardziej bezpieczne dla typu, podczas gdy w mallocogóle nie jest bezpieczne dla typu.

Jedyny sposób, w jaki mógłbym pomyśleć, że byłby korzystny malloc, to gdybyś musiał zmienić rozmiar bufora danych. Słowo newkluczowe nie ma analogicznego sposobu jak realloc. reallocFunkcja może być w stanie bardziej efektywnie rozszerzyć rozmiar fragmentu pamięci dla ciebie.

Warto wspomnieć, że nie można mieszać new/ freei malloc/ delete.

Uwaga: niektóre odpowiedzi w tym pytaniu są nieprawidłowe.

int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
int* p_array  = new int[5];  // Creates 5 elements
Brian R. Bondy
źródło
2
Jeśli chodzi o wywoływanie delete foo, kiedy powinieneś wywołać delete [] foo, niektóre kompilatory naprawią to automatycznie dla ciebie i nie będą przeciekać, a inne usuną tylko pierwszy wpis i wyciek. Miałem kilka z nich w jakimś kodzie i valgrind je dla ciebie znajdzie.
KPexEA
30
Jeśli nie użyjesz poprawnego usunięcia, wynik jest niezdefiniowany . To jest niepoprawne. Fakt, że czasem może się to przydać lub zadziałać, jest po prostu ślepym szczęściem.
Michael Burr,
8
@KPexEA: Nawet jeśli niektóre kompilatory mogą naprawić twoje błędy, nadal źle jest je popełnić :) Zawsze używaj delete [] tam, gdzie jest to właściwe.
korona,
62
„O ile nie jesteś zmuszony do używania C, nigdy nie powinieneś używać malloc. Zawsze używaj nowego.” Dlaczego? Jaka jest tutaj wygrana? W przypadku obiektów potrzebujemy konstrukcji, ale w przypadku bloków pamięci wyraźnie dokumentujesz dwa sposoby popełniania błędów w kodowaniu (łatwiejszy do złapania () vs [] w nowym i mniej trudny do złapania niedopasowany układ vs nowy i skaler skalujący). Jaka jest motywacja do użycia polecenia new / delete dla bloków surowej pamięci?
Ben Supnik
3
@DeadMG: Jeśli ktoś tworzy tablicę do użycia przez asynchroniczną funkcję API, czy nie new[]byłoby znacznie bezpieczniej niż std::vector? Jeśli się użyje new[], jedynym sposobem, w jaki wskaźnik stałby się nieprawidłowy, byłby jawny delete, podczas gdy pamięć przydzielona dla a std::vectormogłaby zostać unieważniona, gdy wektor zostanie zmieniony lub pozostanie w zasięgu. (Należy pamiętać, że podczas korzystania new[]z niej trzeba by dopuścić możliwość, że ktoś nie będzie w stanie zadzwonić, deletejeśli metoda asynchroniczna jest nadal w toku; jeśli konieczne może być porzucenie operacji asynchronicznej, konieczne może być zorganizowanie usunięcia przez wywołanie zwrotne) .
supercat
144

Krótka odpowiedź brzmi: nie używaj mallocdo C ++ bez naprawdę dobrego powodu. mallocma wiele braków, gdy jest używany z C ++, który newzostał zdefiniowany w celu przezwyciężenia.

Niedociągnięcia naprawione przez nowy dla kodu C ++

  1. mallocnie jest bezpieczny w żaden znaczący sposób. W C ++ musisz przesłać zwrot z void*. To potencjalnie wprowadza wiele problemów:

    #include <stdlib.h>
    
    struct foo {
      double d[5];
    }; 
    
    int main() {
      foo *f1 = malloc(1); // error, no cast
      foo *f2 = static_cast<foo*>(malloc(sizeof(foo)));
      foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad
    }
  2. Ale jest gorzej. Jeśli typem jest POD (zwykłe stare dane) , można częściowo rozsądnie użyć mallocdo przydzielenia pamięci dla niego, tak f2jak w pierwszym przykładzie.

    Nie jest to jednak takie oczywiste, jeśli typem jest POD. Istotnym czynnikiem jest fakt, że dany typ może przejść z POD na inny niż POD bez wynikającego błędu kompilatora i potencjalnie bardzo trudnych do debugowania problemów. Na przykład, jeśli ktoś (być może inny programista, podczas konserwacji, znacznie później, zrobiłby zmianę, która spowodowała, że foonie jest już POD, to w czasie kompilacji nie pojawiłby się żaden oczywisty błąd, np.

    struct foo {
      double d[5];
      virtual ~foo() { }
    };

    może sprawić, że mallocz f2również stać się zły, bez żadnych oczywistych diagnostyki. Ten przykład jest trywialny, ale można przypadkowo wprowadzić non-PODness znacznie dalej (np. W klasie bazowej, dodając element inny niż POD). Jeśli masz C ++ 11 / boost, możesz użyć, is_podaby sprawdzić, czy to założenie jest poprawne i spowodować błąd, jeśli nie jest to:

    #include <type_traits>
    #include <stdlib.h>
    
    foo *safe_foo_malloc() {
      static_assert(std::is_pod<foo>::value, "foo must be POD");
      return static_cast<foo*>(malloc(sizeof(foo)));
    }

    Chociaż boost nie jest w stanie określić, czy typ jest POD bez C ++ 11 lub innych rozszerzeń kompilatora.

  3. malloczwraca, NULLjeśli przydział nie powiedzie się. newrzuci std::bad_alloc. Zachowanie późniejszego używania NULLwskaźnika jest niezdefiniowane. Wyjątek ma czystą semantykę, gdy jest zgłaszany i jest wyrzucany ze źródła błędu. Opakowanie mallocz odpowiednim testem przy każdym połączeniu wydaje się nudne i podatne na błędy. (Musisz tylko raz zapomnieć, aby cofnąć całą tę dobrą pracę). Można zezwolić na wyjątek rozprzestrzeniania się na poziom, na którym osoba dzwoniąca jest w stanie rozsądnie go przetworzyć, gdzie NULLznacznie trudniej jest sensownie przekazać. Możemy rozszerzyć naszą safe_foo_mallocfunkcję o zgłoszenie wyjątku lub wyjście z programu lub wywołanie jakiegoś programu obsługi:

    #include <type_traits>
    #include <stdlib.h>
    
    void my_malloc_failed_handler();
    
    foo *safe_foo_malloc() {
      static_assert(std::is_pod<foo>::value, "foo must be POD");
      foo *mem = static_cast<foo*>(malloc(sizeof(foo)));
      if (!mem) {
         my_malloc_failed_handler();
         // or throw ...
      }
      return mem;
    }
  4. Zasadniczo mallocjest funkcją C i newjest funkcją C ++. W rezultacie mallocnie działa ładnie z konstruktorami, a jedynie zajmuje się przydzielaniem fragmentu bajtów. Moglibyśmy rozszerzyć naszą safe_foo_mallocofertę na korzystanie z miejsc docelowych new:

    #include <stdlib.h>
    #include <new>
    
    void my_malloc_failed_handler();
    
    foo *safe_foo_malloc() {
      void *mem = malloc(sizeof(foo));
      if (!mem) {
         my_malloc_failed_handler();
         // or throw ...
      }
      return new (mem)foo();
    }
  5. Nasza safe_foo_mallocfunkcja nie jest bardzo ogólna - idealnie chcielibyśmy czegoś, co poradzi sobie z każdym rodzajem, nie tylko foo. Możemy to osiągnąć za pomocą szablonów i szablonów variadic dla konstruktorów innych niż domyślne:

    #include <functional>
    #include <new>
    #include <stdlib.h>
    
    void my_malloc_failed_handler();
    
    template <typename T>
    struct alloc {
      template <typename ...Args>
      static T *safe_malloc(Args&&... args) {
        void *mem = malloc(sizeof(T));
        if (!mem) {
           my_malloc_failed_handler();
           // or throw ...
        }
        return new (mem)T(std::forward(args)...);
      }
    };

    Teraz jednak naprawiając wszystkie zidentyfikowane dotąd problemy, praktycznie na nowo opracowaliśmy domyślnego newoperatora. Jeśli zamierzasz używać malloci umieszczać, newmożesz równie dobrze użyć newna początek!

Flexo
źródło
27
Szkoda, że ​​C ++ jest wykonany structi classznaczy w zasadzie to samo; Zastanawiam się, czy wystąpiłyby jakiekolwiek problemy z structzarezerwowaniem POD dla POD i ewentualnie classdomniemaniem, że wszystkie typy są POD-POD. Wszelkie typy zdefiniowane przez kod, który był wcześniejszy niż wynalazek C ++, koniecznie byłyby POD, więc nie sądzę, że kompatybilność wsteczna byłaby problemem. Czy istnieją zalety posiadania typy nie-pods zadeklarowane jako structzamiast class?
supercat
1
@supercat Trochę późno, ale jak się okazuje, zrobienie structi classzrobienie prawie tego samego było wspaniałą decyzją projektową, która teraz umożliwia zgrabną funkcję o nazwie „metaklasy” (z Herb) .
Rakete1111,
@ Rakete1111: Na pierwszy rzut oka wygląda na to, że wstępnie przetwarza wersję języka, która używa słów kluczowych z prefiksem dolara, takich jak $class. Nie jestem pewien, co to ma do czynienia z classi structbędących synonimami, jednak.
supercat
@supercat System typów byłby bardziej rozwidlony. Mając classi mając na structmyśli to samo, możesz dokonywać dowolnych przekształceń na nich ( $class) bez obawy , że zrobisz classa structi vice versa.
Rakete1111,
@ Rakete1111: Jeśli pewne rodzaje operacji i transformacji są bezpieczne w przypadku niektórych typów, ale nie innych, posiadanie tego typu bezpośrednio to identyfikuje, a kompilator odrzuca niebezpieczne operacje i transformacje, wydaje się lepsze niż zmiana metaklasy, która była używana w sposoby, które są odpowiednie tylko dla PODS, po cichu zmień na nie-PODS.
supercat
53

Z C ++ FQA Lite :

[16.4] Dlaczego powinienem używać nowego zamiast godnego zaufania starego malloc ()?

FAQ: new / delete wywołuje konstruktor / destruktor; nowy jest bezpieczny dla typu, malloc nie jest; nowy może zostać zastąpiony przez klasę.

FQA: Zalety nowego wymienionego w FAQ nie są cnotami, ponieważ konstruktory, destruktory i przeciążenie operatora są śmieciami (zobacz, co się stanie, gdy nie będziesz mieć śmiecia?), A problem bezpieczeństwa typu jest tutaj naprawdę niewielki (zwykle masz rzucić void * zwróconą przez malloc na odpowiedni typ wskaźnika, aby przypisać go do zmiennej typu wskaźnik, która może być denerwująca, ale daleka od „niebezpiecznej”).

Aha, a użycie godnego zaufania starego malloc umożliwia użycie równie godnego zaufania i starego realloc. Szkoda, że ​​nie mamy nowego, odnowionego operatora ani czegoś takiego.

Mimo to nowy nie jest wystarczająco zły, aby uzasadnić odstępstwo od powszechnego stylu używanego w całym języku, nawet jeśli jest to C ++. W szczególności klasy z nietrywialnymi konstruktorami źle zachowują się, jeśli po prostu malloc obiekty. Dlaczego więc nie użyć nowego w całym kodzie? Ludzie rzadko przeciążają operatora nowym, więc prawdopodobnie nie będzie to zbytnio przeszkadzało. A jeśli przeciążą nowe, zawsze możesz poprosić ich, aby przestali.

Przepraszam, po prostu nie mogłem się oprzeć. :)

Matthias Benkard
źródło
7
To zamieszki ! Dzięki.
dmckee --- były kot moderator
8
Nie mogę potraktować tego komentarza poważnie, ponieważ wyraźnie rzutuje on na stronnictwo autora względem C ++. C ++ jest językiem używanym do tworzenia oprogramowania zorientowanego na wydajność, a moduł wyrzucania elementów bezużytecznych może być szkodliwy dla jego celu. Nie zgadzam się z całą twoją odpowiedzią!
Miguel
1
@Miguel Przegapiłeś żart.
Dan Bechard
50

Zawsze używaj nowego w C ++. Jeśli potrzebujesz bloku nietypowej pamięci, możesz bezpośrednio użyć operatora new:

void *p = operator new(size);
   ...
operator delete(p);
Ferruccio
źródło
3
co ciekawe, zawsze po prostu przydzielałem tablicę znaków bez znaku, gdy potrzebuję takiego bufora danych surowych.
Greg Rogers
Ostrożnie semmantyka powinna wyglądać tak: p_var = nowy typ (inicjator); Nie rozmiar.
Brian R. Bondy,
11
Nie, jeśli zadzwonisz bezpośrednio do operatora new, to liczba bajtów musi zostać przydzielona jako parametr.
Ferruccio,
1
Nie jestem pewien, nigdy nie słyszałem o tej składni.
Brian R. Bondy,
9
Przeciwieństwem operator newjest operator delete. Wywołanie deletewyrażenia typu nie jest dobrze zdefiniowaną czynnością void*.
CB Bailey,
33

Używaj malloci tylko do przydzielania pamięci, która będzie zarządzana przez biblioteki i interfejsy API ukierunkowane na c. Użyj i (i wariantów) do wszystkiego, co kontrolujesz.free newdelete[]

dmckee --- były kot moderator
źródło
10
Zauważ też, że dobrze napisana biblioteka C ukryje malloc i bezpłatnie wewnętrznie, tak powinien działać programista C.
Dacav,
@dmckee, czy masz przykład C ++ używającego bibliotek c-centrycznych przez malloc i za darmo?
milesma
1
@Dacav: Jeśli funkcja C zaakceptuje wskaźnik do obiektu, którego będzie musiał używać po powrocie funkcji, a osoba wywołująca nie będzie mogła wiedzieć, kiedy obiekt jest nadal potrzebny, byłoby to całkowicie uzasadnione dla funkcji aby określić, że wskaźnik musi zostać utworzony za pomocą malloc. Podobnie, jeśli taka funkcja strdupmusi utworzyć obiekt i zwrócić go do obiektu wywołującego, całkowicie uzasadnione jest określenie, że obiekt wywołujący musi wywoływać freeobiekt, gdy nie jest on już potrzebny. Jak takie funkcje mogą uniknąć ujawnienia dzwoniącemu korzystania z malloc / free?
supercat,
@ supercat, jest coś z natury złego w tym, że funkcja C akceptuje wskaźnik do obiektów, ponieważ C w ogóle nie jest świadomy obiektów. Generalnie uważam, że najlepszym podejściem jest stosowanie owijania semantycznego wokół alokacji / dezalokacji również w C. Może to być nadal akceptowalne, ale mniej elastyczne, jeśli biblioteka C prosi dzwoniącego o wstępne przydzielenie i / lub cofnięcie alokacji pamięci. Jeśli funkcja C robi to i rości sobie prawo własności do przydzielonej pamięci, niejawnie wymagane jest przydzielenie jej za pomocą malloc.
Dacav,
@supercat Jednym z przykładów codziennego pakietu, z którego jestem pewien, że wszyscy korzystali, jest libgmp. Jeśli kiedykolwiek korzystałeś z dowolnego szyfrowania typu open source lub oprogramowania opartego na takim szyfrowaniu (co jest bardzo prawdopodobne), prawdopodobnie korzystałeś z biblioteki arytmetycznej o dowolnej precyzji, która musi się powiększać i zmniejszać własne dane wewnętrzne. Odbywa się to za pomocą funkcji inicjalizacji ... i wtedy zastanawiasz się, jak korzystać z kodu C, który jest libgmp, w C ++, bez ponownej kompilacji w C ++? Mając to na uwadze (linker), pomyśl o tym ... dlaczego jakakolwiek rozsądna osoba miałaby kiedykolwiek używać mallocC ++?
autystyczny
31

nowy vs malloc ()

1) newjest operatorem , natomiast malloc()jest funkcją .

2) newwywołuje konstruktorów , podczas gdy malloc()nie.

3) newzwraca dokładny typ danych , a malloc()zwraca void * .

4) newnigdy nie zwraca NULL (rzuci w przypadku niepowodzenia), podczas gdy malloc()zwraca NULL

5) Realokacja pamięci nie obsługiwana przez newczas malloc()może

Yogeesh HT
źródło
6
Cześć, w punkcie 4) nowy może zostać poinstruowany, aby zwracał NULL w przypadku awarii. char* ptr = new (std::nothrow) char [323232];
Singh,
1
6) nowe tworzy z argumentów konstruktora, podczas gdy malloc używa rozmiaru.
Evan Moran
jest też newfunkcja
Ma Ming
Jeśli byłeś tak skłonny w C, aby dokonać realokacji , mam nadzieję, że wolisz użyć realloczamiast malloci zacząć od zainicjowanej zmiennej wskaźnika NULL. Z drugiej strony, jeśli chcesz zmienić rozmiar pamięci w C ++, sugerowałbym, std::vectorw przeciwieństwie do realloc... Tego lub pliku.
autystyczny
19

Aby odpowiedzieć na twoje pytanie, powinieneś znać różnicę między mallocinew . Różnica jest prosta:

malloc alokuje pamięć , a new alokuje pamięć ORAZ wywołuje konstruktor obiektu, dla którego alokujesz pamięć.

Tak więc, chyba że jesteś ograniczony do C, nigdy nie powinieneś używać malloc, szczególnie w przypadku obiektów C ++. To byłby przepis na zerwanie z programem.

Również różnica między freei deletejest taka sama. Różnica polega na tym delete, że oprócz zwalniania pamięci wywoła niszczyciel obiektu.

Fizyk kwantowy
źródło
13

Jest jedna duża różnica między malloci new. mallocprzydziela pamięć. Jest to w porządku dla C, ponieważ w C bryła pamięci jest obiektem.

W C ++, jeśli nie masz do czynienia z typami POD (które są podobne do typów C), musisz wywołać konstruktor w miejscu pamięci, aby faktycznie mieć tam obiekt. Typy inne niż POD są bardzo popularne w C ++, ponieważ wiele funkcji C ++ powoduje, że obiekt automatycznie nie jest POD.

newprzydziela pamięć i tworzy obiekt w tej lokalizacji pamięci. W przypadku typów innych niż POD oznacza to wywołanie konstruktora.

Jeśli zrobisz coś takiego:

non_pod_type* p = (non_pod_type*) malloc(sizeof *p);

Uzyskanego wskaźnika nie można usunąć, ponieważ nie wskazuje on obiektu. Trzeba będzie wywołać na nim konstruktora, zanim będzie można go użyć (i odbywa się to za pomocą umieszczania new).

Z drugiej strony, jeśli:

non_pod_type* p = new non_pod_type();

Otrzymasz wskaźnik, który jest zawsze poprawny, ponieważ newutworzył obiekt.

Nawet w przypadku typów POD istnieje znacząca różnica między tymi dwoma:

pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;

Ten fragment kodu wypisuje nieokreśloną wartość, ponieważ utworzone obiekty POD mallocnie są inicjowane.

Za pomocą newmożna określić konstruktora do wywołania, a tym samym uzyskać dobrze zdefiniowaną wartość.

pod_type* p = new pod_type();
std::cout << p->foo; // prints 0

Jeśli naprawdę tego chcesz, możesz użyć narzędzia newdo uzyskania niezainicjowanych obiektów POD. Zobacz tę inną odpowiedź, aby uzyskać więcej informacji na ten temat.

Kolejną różnicą jest zachowanie po awarii. Gdy nie uda się przydzielić pamięci, malloczwraca wskaźnik zerowy, a newzgłasza wyjątek.

Pierwszy z nich wymaga przetestowania każdego wskaźnika zwróconego przed jego użyciem, a drugi zawsze będzie generował prawidłowe wskaźniki.

Z tych powodów w kodzie C ++ powinieneś używać new, a nie malloc. Ale nawet wtedy nie powinieneś używać new„na otwartej przestrzeni”, ponieważ zdobywa zasoby, które musisz później uwolnić. Po użyciu newnależy natychmiast przekazać jego wynik do klasy zarządzającej zasobami:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
R. Martinho Fernandes
źródło
7

Alokacja dynamiczna jest wymagana tylko wtedy, gdy czas życia obiektu powinien być inny niż zakres, w którym został utworzony (dotyczy to również zmniejszenia zakresu jako większego), a masz konkretny powód, dla którego nie jest przechowywany według wartości praca.

Na przykład:

 std::vector<int> *createVector(); // Bad
 std::vector<int> createVector();  // Good

 auto v = new std::vector<int>(); // Bad
 auto result = calculate(/*optional output = */ v);
 auto v = std::vector<int>(); // Good
 auto result = calculate(/*optional output = */ &v);

Od C ++ 11 mamy std::unique_ptrdo czynienia z przydzieloną pamięcią, która zawiera własność przydzielonej pamięci. std::shared_ptrzostał stworzony, gdy musisz współdzielić własność. (będziesz potrzebować tego mniej niż można oczekiwać w dobrym programie)

Utworzenie instancji staje się naprawdę łatwe:

auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11

C ++ 17 dodaje również, std::optionalco może zapobiec wymaganiu przydziału pamięci

auto optInstance = std::optional<Class>{};
if (condition)
    optInstance = Class{};

Gdy tylko „instancja” zniknie z zakresu, pamięć zostanie wyczyszczona. Przeniesienie własności jest również łatwe:

 auto vector = std::vector<std::unique_ptr<Interface>>{};
 auto instance = std::make_unique<Class>();
 vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)

Więc kiedy nadal potrzebujesz new? Prawie nigdy od wersji C ++ 11. Większość z nich używasz, std::make_uniquedopóki nie dojdziesz do punktu, w którym trafisz na interfejs API, który przenosi własność za pomocą surowych wskaźników.

 auto instance = std::make_unique<Class>();
 legacyFunction(instance.release()); // Ownership being transferred

 auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr

W C ++ 98/03 musisz ręcznie zarządzać pamięcią. W takim przypadku spróbuj zaktualizować standard do nowszej wersji. Jeśli utkniesz:

 auto instance = new Class(); // Allocate memory
 delete instance;             // Deallocate
 auto instances = new Class[42](); // Allocate memory
 delete[] instances;               // Deallocate

Upewnij się, że śledzisz własność poprawnie, aby nie było wycieków pamięci! Semantyka przenoszenia także jeszcze nie działa.

Kiedy więc potrzebujemy malloc w C ++? Jedynym uzasadnionym powodem byłoby przydzielenie pamięci i zainicjowanie jej później poprzez umieszczenie nowego.

 auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
 auto instance = new(instanceBlob)Class{}; // Initialize via constructor
 instance.~Class(); // Destroy via destructor
 std::free(instanceBlob); // Deallocate the memory

Chociaż powyższe jest ważne, można to zrobić również za pośrednictwem nowego operatora. std::vectorjest tego dobrym przykładem.

Wreszcie mamy jeszcze słonia w pokoju: C. Jeśli musisz pracować z biblioteką C, w której pamięć jest przydzielana w kodzie C ++ i uwalniana w kodzie C (lub na odwrót), musisz użyć malloc / free.

Jeśli tak jest, zapomnij o funkcjach wirtualnych, funkcjach członkowskich, klasach ... Dozwolone są tylko struktury z POD.

Niektóre wyjątki od zasad:

  • Piszesz standardową bibliotekę z zaawansowanymi strukturami danych, w których odpowiedni jest malloc
  • Musisz przeznaczyć dużą ilość pamięci (w kopii pliku 10 GB?)
  • Masz oprzyrządowanie uniemożliwiające korzystanie z niektórych konstrukcji
  • Musisz zapisać niekompletny typ
JVApen
źródło
6

Jest kilka rzeczy, które newtego mallocnie robią:

  1. new konstruuje obiekt, wywołując konstruktor tego obiektu
  2. new nie wymaga rzutowania przydzielonej pamięci.
  3. Nie wymaga alokacji ilości pamięci, a raczej wymaga zbudowania pewnej liczby obiektów.

Tak więc, jeśli używasz malloc, musisz wyraźnie robić powyższe rzeczy, co nie zawsze jest praktyczne. Dodatkowo newmoże być przeciążony, ale mallocnie może być.

herohuyongtao
źródło
5

Jeśli pracujesz z danymi, które nie wymagają budowy / zniszczenia i wymagają ponownego przydzielenia (np. Duży wachlarz int), to uważam, że malloc / free to dobry wybór, ponieważ daje ci realokację, która jest znacznie szybsza niż new-memcpy -delete (jest na moim Linux-ie, ale myślę, że może to zależeć od platformy). Jeśli pracujesz z obiektami C ++, które nie są POD i wymagają budowy / zniszczenia, musisz użyć nowych i usuń operatory.

W każdym razie nie rozumiem, dlaczego nie powinieneś używać obu (pod warunkiem, że zwolnisz swoją malloced pamięć i usuniesz obiekty przydzielone z nowym), jeśli możesz skorzystać z przyspieszenia (czasami znaczącego, jeśli przenosisz duże tablice POD), które może dać ci realloc.

O ile nie jest to potrzebne, powinieneś trzymać się nowego / delete w C ++.

PSkocik
źródło
3

Jeśli masz kod C, który chcesz przenieść do C ++, możesz zostawić w nim wszelkie wywołania malloc (). W przypadku każdego nowego kodu C ++ polecam użycie nowego.

Fred Larson
źródło
3

Jeśli używasz C ++, spróbuj użyć nowego / delete zamiast malloc / calloc, ponieważ są operatorami. W przypadku malloc / calloc musisz dołączyć kolejny nagłówek. Nie mieszaj dwóch różnych języków w tym samym kodzie. Ich praca jest podobna pod każdym względem, obie alokują pamięć dynamicznie z segmentu sterty w tablicy skrótów.

użytkownik3488100
źródło
2

new zainicjuje wartości domyślne struktury i poprawnie połączy odnośniki z nim samym.

Na przykład

struct test_s {
    int some_strange_name = 1;
    int &easy = some_strange_name;
}

new struct test_sZwróci więc zainicjowaną strukturę z działającym odwołaniem, podczas gdy wersja Malloc'ed nie ma wartości domyślnych, a odniesienia do internowania nie są inicjowane.

lama12345
źródło
1

Z niższej perspektywy, nowa zainicjuje całą pamięć przed jej przekazaniem, podczas gdy malloc zachowa oryginalną zawartość pamięci.

Peiti Li
źródło
4
new na ogół nie inicjuje pamięci, choć istnieją sposoby, aby tak się stało: patrz stackoverflow.com/questions/2204176/..., aby uzyskać jedną dyskusję na ten temat.
wjl 30.09.11
0

W poniższym scenariuszu nie możemy użyć nowego, ponieważ wywołuje on konstruktor.

class  B  {
private:
    B *ptr;
    int x;
public:
    B(int n)  {
        cout<<"B: ctr"<<endl;
        //ptr = new B;  //keep calling ctr, result is segmentation fault
        ptr = (B *)malloc(sizeof(B));
        x = n;
        ptr->x = n + 10;
    }
    ~B()  {
        //delete ptr;
        free(ptr);
        cout<<"B: dtr"<<endl;
    }
};
Barry
źródło
0

Te newi deleteoperatorzy mogą pracować na zajęciach i struktur, podczas gdy malloci freetylko praca z bloków pamięci, które muszą być odlewane.

Użycie new/deletepomoże ulepszyć kod, ponieważ nie będziesz musiał rzutować przydzielonej pamięci do wymaganej struktury danych.

selwyn
źródło
0

Rzadkim przypadkiem, w którym należy rozważyć użycie malloc / free zamiast new / delete, jest przydzielanie, a następnie ponowne przydzielanie (proste typy pod, nie obiekty) za pomocą realloc, ponieważ nie ma podobnej funkcji do realloc w C ++ (chociaż można to zrobić za pomocą więcej podejścia C ++).

Florentino Tuason
źródło
-4

malloc () służy do dynamicznego przydzielania pamięci w C, podczas gdy ta sama praca jest wykonywana przez new () w c ++. Dlatego nie można mieszać konwencji kodowania 2 języków. Byłoby dobrze, gdybyś poprosił o różnicę między calloc a malloc ()

Hitesh Ahuja
źródło
2
Państwo może (ale prawie zawsze nie powinno) używać mallocw C ++.
interjay
1
Przegapiłeś także najważniejszy punkt, którym powinieneś dążyć do uniknięcia dynamicznej alokacji pamięci, chyba że robisz to za pomocą inteligentnych wskaźników. Po prostu nastawiasz się na ból w inny sposób
thecoshman