센로그

[EC++] 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 본문

Effective/Effective C++

[EC++] 5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자

seeyoun 2024. 11. 13. 22:01

내가 직접 만들지 않으면 컴파일러가 자동으로 만들어주는 것들

컴파일러는 경우에 따라 클래스에 대해 다음 함수들을 암시적으로 정의한다.

  • 생성자
  • 복사 생성자
  • 복사 대입 연산자
  • 소멸자

 

즉, 우리가 다음과 같이 선언했다면

class Empty {};

 

이렇게 쓴 것과 똑같다는 것이다.

class Empty
{
    public:
        Empty(){...}                            // 기본 생성자
        Empty(const Empty &rhs){...}            // 복사 생성자
        ~Empty(){...}                           // 소멸자
        Empty& operator=(const Empty &rhs){...} // 복사 대입 연산자
};

이들은 기본적으로 public, inline으로 선언된다.

 


기본 클래스의 소멸자가 비가상 소멸자라면

  • 파생 클래스의 소멸자도 비가상 소멸자로 만들어진다.

 

기본 클래스의 소멸자가 가상 소멸자라면

  • 파생 클래스의 소멸자도 가상 소멸자로 만들어진다.

 


참조자 멤버가 있는 클래스의 복사 생성자 및 복사 대입 연산자

  • 참조자 멤버가 있는 클래스는 복사 대입 연산자 사용이 어렵거나 의미가 없는 경우가 많으므로, 복사 대입 연산자를 delete하여 사용하지 않도록 하는 것이 일반적이다.
class MyClass {
public:
    int& ref; // 참조 멤버

    // 생성자로 참조 멤버 초기화
    MyClass(int& r) : ref(r) {}

    // 복사 대입 연산자를 삭제하여 사용하지 못하게 함
    MyClass& operator=(const MyClass&) = delete;
};
  • 또는 private 멤버로 선언하여 외부로부터의 호출을 차단할 수 있다.
    • 선언만 해놓고 정의는 안하는 것이 좋다. 후에 실수로 호출하려 했더라도 링크 시점에 에러를 확인할 수 있기 때문이다.

 


결론

  • 컴파일러가 몰래 만든 함수들로 인해 예상치 못한 오류가 발생할 수 있다는 점을 기억하자.
    • 가상 소멸자가 생성되어야 하는데, 비가상 소멸자가 생성될 수 있다.
    • 몰래 만들어진 복사 생성자로 인해서 예상치 못한 동작을 할 수 있다.
Comments