programowanieProgramista C++

Co się dzieje, gdy przekazujemy obiekt klasy przez wartość do funkcji i jakie pułapki należy uwzględnić przy implementacji konstruktora kopiującego?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Przy przekazywaniu obiektu przez wartość do funkcji w C++ następuje tworzenie kopii obiektu za pomocą konstruktora kopiującego. Jeżeli w klasie zdefiniowany jest użytkownikowy konstruktor kopiujący, jest on wywoływany do inicjalizacji tymczasowego obiektu-argumentu funkcji. Jeżeli nie jest zdefiniowany — używany jest konstruktor domyślny przez kompilator, który wykonuje kopiowanie bitowe (shallow copy).

Pułapki:

  • Jeżeli klasa zarządza zasobami (np. ma surowe wskaźniki), domyślnie generowany konstruktor kopiujący prowadzi do błędów podwójnego zwolnienia pamięci i wycieków.
  • Należy upewnić się, że konstruktor kopiujący poprawnie kopiuje zasoby (zwykle deep copy).

Przykład:

class StringWrapper { char* data; public: StringWrapper(const char* str) { data = new char[strlen(str) + 1]; strcpy(data, str); } // Błąd: shallow copy StringWrapper(const StringWrapper& other) : data(other.data) {} ~StringWrapper() { delete [] data; } }; void foo(StringWrapper s) { // ... } int main() { StringWrapper s1("hello"); foo(s1); // UB!!! return 0; }

Pytanie z podstępem

"Co się stanie, jeśli zdefiniujemy konstruktor kopiujący w klasie z wskaźnikiem w ten sposób: MyClass(const MyClass &other) : data(other.data) {}? Jakie konsekwencje to wywołuje?"

Właściwa odpowiedź: Taki konstruktor kopiujący stworzy obiekt z wskaźnikiem na tę samą przestrzeń pamięci, co kopiowany. Przy zniszczeniu dwóch obiektów pamięć zostanie zwolniona dwukrotnie (double free), co prowadzi do nieokreślonego zachowania. Należy zaimplementować deep copy:

MyClass(const MyClass &other) { data = new int(*other.data); }

Przykłady rzeczywistych błędów z powodu braku wiedzy na temat szczegółów tematu


Historia

W dużym projekcie serwerowym użyto kontenerów z obiektami z "surową" tablicą wewnątrz i standardowym konstruktorem kopiującym (shallow copy). Przy przekazywaniu obiektów przez wartość wystąpił double free i awarie aplikacji, które były uchwytywane tylko w produkcji.


Historia

W starej bibliotece C++ do pracy z obrazami konstruktor kopiujący nie kopiował bufora grafiki, co prowadziło do zmian jednej kopii obrazu przy zmianie innej i nieoczekiwanych błędów w interfejsie.


Historia

Przy zwracaniu obiektu przez wartość z funkcji w jednym z wewnętrznych systemów zarządzania hasłami dane były czyszczone przy niszczeniu obiektu tymczasowego (shallow copy), w wyniku czego rzeczywisty obiekt przechowywał zainicjowany wskaźnik, a wyciek ujawnił się przypadkowo w wyniku audytu bezpieczeństwa.