객체 복사 금지와 그 이유
객체의 복사 금지 방법들
- 복사 생성자 / ‘=’ 연산자 오버로딩에 대한
= delete처리
- delete 는 new 와 같이 사용하는 ‘메모리 해제’ 연산자로서의 기능도 있으나
함수 뒤에 ‘= delete’를 통해 원치 않은 함수를 삭제할 수 있음
(C++ 11)
- 컴파일러의 원치 않은 특정 함수들을 만드는 것을 예방함
- 해당 함수 사용 시도시, 컴파일 에러 발생
(‘삭제된 함수’ 사용 시도)
- 컴파일러의 원치 않은 특정 함수들을 만드는 것을 예방함
- delete 는 new 와 같이 사용하는 ‘메모리 해제’ 연산자로서의 기능도 있으나
class FileHandle {
public:
FileHandle(const char* path);
~FileHandle();
FileHandle(const FileHandle&) = delete; // 복사 생성 금지
FileHandle& operator=(const FileHandle&) = delete; // 복사 대입 금지
// 필요하다면 move만 허용
FileHandle(FileHandle&&) noexcept;
FileHandle& operator=(FileHandle&&) noexcept;
};
- 복사 생성자/대입에 private 처리하기
- C++ 11 이전에 사용 가능한 방식
외부에서 접근하려 하면 컴파일 에러 발생
(‘private 접근’ 시도)
- C++ 11 이전에 사용 가능한 방식
class NonCopy {
public:
NonCopy() = default;
private:
NonCopy(const NonCopy&); // 선언만 하고
NonCopy& operator=(const NonCopy&); // 정의는 안 함
};
- Unique_ptr 같은 ‘복사 불가’ 관련 멤버를 포함하기
- 컴파일러가 해당 클래스의 복사 연산들을 제거
- 컴파일러가 해당 클래스의 복사 연산들을 제거
class Owner {
std::unique_ptr<int> Ptr; // unique_ptr 자체가 복사 불가
public:
Owner() : Ptr(std::make_unique<int>(10)) {}
// 별도 조치 안 해도 Owner는 복사 불가 타입이 됨
};
객체 복사를 막는 이유
- 소유권(OwnerShip) & 자원 관리
- 안정성 과 성능
이유 1. 소유권 (OwnerShip) & 자원
- ‘하나의 객체’만이 가져야 하는,
한 객체만 존재해야 하는 상황
- Thread 와 동기화 도구 쪽이 대표적인 예시
- 디자인 패턴 쪽에서는 ‘싱글톤’도 비슷한 경우가 될 수 있음
- Thread 와 동기화 도구 쪽이 대표적인 예시
-
이러한 경우는
객체의 ‘복사 생성/대입’ 가능성을 막아
‘소유권’ 개념을 명확히! - 이전에 배웠던 RAII 와 관련됨
- ‘한 객체가 오로지 소유하는 것’이란 부분에서
소유권과 연관됨 - RTTI? RAII?
- ‘한 객체가 오로지 소유하는 것’이란 부분에서
이유 2. 안정성 과 성능
안정성
-
‘얕은 복사’ 문제를 해결
깊은/얕은 복사? -
단순한 생포인터를 그대로 복사하는 경우
서로 같은 메모리를 가리키기에
둘 중 하나만 delete 하더라도
남은 쪽이 댕글리 포인터가 되거나
중복 delete 문제가 발생 -
그렇기에 Unique_Ptr 등으로 복사를 막거나
Shared_Ptr 사용을 권장하여 ‘참조’하도록 함
성능
- 지나치게 큰 배열 이나 큰 크기의 리소스는
‘값을 복사’하는 경우, 연산량과 메모리 소모를 증가
- 특히 ‘지역변수’로 먼저 만들고 복사하는 경우
이러한 문제가 두드러짐
- 특히 ‘지역변수’로 먼저 만들고 복사하는 경우
- 아예 복사를 막거나
Move를 통해 이미 만든 개체의 소유권을 이동시켜
성능을 개선할 수 있음
댓글남기기