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_ptr
do czynienia z przydzieloną pamięcią, która zawiera własność przydzielonej pamięci. std::shared_ptr
został 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::optional
co 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_unique
dopó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::vector
jest 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