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:
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; }
"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); }
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.