KoreanFoodie's Study

Effective C++ | 항목 5 : C++ 가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 본문

Tutorials/C++ : Advanced

Effective C++ | 항목 5 : C++ 가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자

GoldGiver 2022. 10. 25. 16:03

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 5 : C++ 가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자

핵심 :

컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들어 놓을 수 있다


클래스를 만들 때, 컴파일러는 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 생성한다. 소멸자는 해당 클래스가 상속한 기본 클래스의 소멸자가 가상 소멸자로 되어 있지 않으면 역시 비가상 소멸자로 만들어진다(물론 사용자가 생성자를 선언하면 컴파일러가 기본 생성자를 만들지 않는다).
그렇다면 이러한 기본 생성자가 제대로 작동하지 않는 경우는 언제일까? 예시를 보자.

template<class T>
class NamedObject
{
  public:
    // 이 생성자는 이제 상수 타입의 name 을 취하지 않는다
    // nameValue 가 비상수 string 의 참조자가 되었기 때문이다.
    // 참조할 string 을 가져야 하기 때문에
    // char* 는 없애 버렸다.
    NamedObject(string& name, const T& value)
    : nameValue(name),
    objectValue(value) 
    {}
    
    ...
    // operator= 는 선언된 게 없다고 가정한다
    
  private:
    std::string& nameValue;
    const T objectValue;
};

자, 이제 다음과 같은 코드를 실행했다고 해보자.

std::string newDog("Persephone");
std::string oldDog("Satch");

NamedObject<int> p(newDog, 2);
NamedObject<int> s(oldDog, 36);

// 어떤 일이 일어날까?
p = s;

위의 코드를 실행시키면, 다음과 같은 에러가 뜬다.

위에서, 우리는 nameValue 를 참조자로 선언했다. 즉, 기본 복사 대입 연산자는 참조자가 가리키는 것을 바꾸려고 시도할 것이므로, 컴파일러는 이러한 경우를 처리하는 것을 '거부' 한다. 왜냐하면, C++ 에서 참조자는 원래 자신이 참조하고 있는 것과 다른 객체를 참조할 수 없기 때문이다!
위에서는 objectValue 의 경우에서, const 인 멤버의 값을 바꾸려고 하는 것도 안된다는 에러 메시지를 출력하고 있다.

 

Comments