C++에서 객체를 값으로 함수에 전달할 때 복사 생성자를 사용하여 객체의 복사본을 생성하는 일이 발생합니다. 클래스에 사용자 정의 복사 생성자가 정의된 경우, 함수의 임시 객체 인수를 초기화하기 위해 호출됩니다. 정의되지 않은 경우, 컴파일러의 기본값이 사용되며, 이는 비트별 복사를 수행합니다(얕은 복사).
함정:
예:
class StringWrapper { char* data; public: StringWrapper(const char* str) { data = new char[strlen(str) + 1]; strcpy(data, str); } // 오류: 얕은 복사 StringWrapper(const StringWrapper& other) : data(other.data) {} ~StringWrapper() { delete [] data; } }; void foo(StringWrapper s) { // ... } int main() { StringWrapper s1("hello"); foo(s1); // UB!!! return 0; }
"포인터가 있는 클래스에서 복사 생성자를 다음과 같이 정의하면 어떻게 되나요:
MyClass(const MyClass &other) : data(other.data) {}? 어떤 결과를 초래하나요?"
정답: 이러한 복사 생성자는 복사된 객체와 동일한 메모리 영역을 가리키는 객체를 생성합니다. 두 객체가 파괴될 때 메모리가 두 번 해제되어 이중 해제(double free)가 발생하여 미정의 동작으로 이어집니다. 깊은 복사를 구현해야 합니다:
MyClass(const MyClass &other) { data = new int(*other.data); }
이야기
대규모 서버 프로젝트에서 "원시" 배열이 내부에 포함된 객체의 컨테이너를 사용하고 기본 복사 생성자(얕은 복사)를 사용했습니다. 객체를 값으로 전달할 때 이중 해제가 발생하여 애플리케이션이 충돌하여 프로덕션에서만 감지되었습니다.
이야기
낡은 C++ 이미지 작업 라이브러리에서 복사 생성자가 그래픽 버퍼를 복사하지 않아 하나의 이미지 복사본을 변경할 때 다른 복사본도 변경되는 예기치 않은 버그가 인터페이스에서 발생했습니다.
이야기
함수에서 값을 통해 객체를 반환할 때 비밀번호를 저장하는 내부 시스템에서 임시 객체(얕은 복사)가 파괴될 때 데이터가 지워져 실제 객체가 초기화된 포인터를 저장하게 되었고, 메모리 누수는 보안 감사 과정에서 우연히 발견되었습니다.