Zasada trzech: jeśli klasa zarządza zasobem (na przykład pamięcią), to przy jawnej implementacji jednego z następujących metod należy zaimplementować także pozostałe:
C++11: Zasada pięciu – dodano operacje przenoszące:
Naruszenie tej zasady prowadzi do błędów w zarządzaniu zasobami, takich jak podwójne usunięcie lub wycieki pamięci.
class Buffer { char* data; public: Buffer(size_t sz) : data(new char[sz]) {} ~Buffer() { delete[] data; } Buffer(const Buffer& other) : data(new char[strlen(other.data)+1]) { strcpy(data, other.data); } Buffer& operator=(const Buffer& other) { if (&other != this) { delete[] data; data = new char[strlen(other.data)+1]; strcpy(data, other.data); } return *this; } // semantyka move w C++11+: Buffer(Buffer&& other) noexcept : data(other.data) { other.data = nullptr; } Buffer& operator=(Buffer&& other) noexcept { if (&other != this) { delete[] data; data = other.data; other.data = nullptr; } return *this; } };
Czy wystarczy zaimplementować tylko destruktor, jeśli klasa zarządza wskaźnikiem?
Nie. Bez operacji kopiujących i przenoszących przy kopiowaniu dojdzie do podwójnego usunięcia pamięci. Na przykład:
Buffer a(10); Buffer b = a; // b i a będą usuwać ten sam wskaźnik!
Historia
W platformie agregacji danych dla telekomunikacji wszystkie klasy implementowały tylko destruktor. Po refaktoryzacji struktury miało miejsce masowe kraknięcie na podwójnym usunięciu: kopiowanie obiektów powodowało losowe zachowania.
Historia
W projekcie gry mobilnej zapomniano zaimplementować konstruktor przenoszący dla klas kontenerów bufora. Przy przenoszeniu obiekt był kopiowany, co powodowało dodatkowe kopiowanie danych i spadki wydajności.
Historia
W bibliotece serializacji struktur danych podczas zwracania obiektu z funkcji zwracany był obiekt tymczasowy, a konstruktor kopiujący wykonywał płytkie kopiowanie wskaźnika. Powstało wiele wycieków, które ujawniły się dopiero po miesiącach pracy.