Po pierwsze, masz wyciek pamięci. Ale prawdopodobnie nie o to chodzi. Załóżmy więc, że masz destruktor, który deallocuje tablicę.
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
~Array() { delete[] buff; }
};
Teraz to szczególności kod jest całkowicie bezpieczne. Nie można zgłaszać wyjątków podczas przypisywania wartości n_
, kolejność inicjowania jest poprawna, a buff
jest jedynym nieprzetworzonym wskaźnikiem w klasie. Jednak wraz z rozpoczęciem rozszerzania klasy i pisania kolejnych zajęć wzrasta ryzyko wycieku pamięci.
Wyobraźmy sobie, że trzeba dodać jeden więcej członków do class Array
:
template<typename Type>
class Array {
size_t n;
Type* buff;
SomethingElse xyz;
public:
Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {}
~Array() { delete[] buff; }
};
Jeżeli konstruktor SomethingElse
rzuca, pamięć przydzielona dla buff
będzie przeciekać, ponieważ destruktor ~Array()
nigdy nie zostanie wywołana.
Nowoczesne C++ nazywa wskaźniki takie jak Type* buff
surowych wskaźników ponieważ jesteś odpowiedzialny za dealokując przechowywanie siebie (biorąc pod uwagę wyjątki), a także wprowadza narzędzia, takie jak std::unique_ptr
i std::shared_ptr
że może zająć dealokacji pamięci automatycznie.
We współczesnym C++ można napisać swoją klasę tak:
template<typename Type>
class Array {
size_t n;
std::unique_ptr<Type[]> buff;
public:
Array(size_t n_): n(n_), buff(new Type[n_]) {}
};
Zawiadomienie braku destructor. unique_ptr
zajmie się dla ciebie dzwonieniem pod numer delete
.
Zauważ też nie zależność od członków klasy wewnątrz listy inicjatora (po prostu pisząc new Type[n_]
zamiast new Type[n]
czyni kod bardziej wydajny)
Na początek nie masz destruktora, a to może prowadzić do wycieku pamięci –
@ArnavBorborah, ale czy to jedyna * niebezpieczna * rzecz? – Loay