이번 항목에선 암묵적으로 생성되는 함수들 때문에 생길 수 있는 문제점들을 짚어 보고, 문제점을 없애기 위해서 어떻게 해야 하는지에 대해서 배워 본다.

1 ) 클래스 정의 할 때, 암묵적으로 생성되는 함수들은 무엇이 있으며, 이 함수들을 암묵적으로 생성하는 것에 대한 의미와 암묵적인 함수들 때문에 생길 수 있는 문제점들을 구체적으로 설명하라.

암묵적으로 생성 되는 함수들은 무엇이 있는가?
a. 기본 생성자
b. 복사 생성자
c. 복사 배정 연산자
d. 소멸자


언제 암묵적으로 생성 될까?

각 암묵적으로 생성되는 함수들을 정의하지 않은 상태에서, 각 함수들을 호출하는 코드를 작성하게 되면, 컴파일러가 각 함수들을 만들어 준다. 예외적으로, 생성자를 단 1개라도 정의하면, 생성자에 대해선 만들어 주지 않고, 복사 배정 연산자 역시 1개라도 정의해 두면, 만들어 주지 않는다.


그렇다면, 이런 함수들 때문에 생길 수 있는 문제점은 무엇이 있는가?

암묵적으로 생성되는 함수들 때문에, 생길 수 있는 문제점은 공통적으로 "예외 명세가 상속되는 문제"가 있다.


이 처럼, Derived 에선, 암묵적으로 생성되는 함수들은 무자비하게 예외까지 상속시켜 준다. 애초에 예외를 제한적으로 던짐으로써, 상태를 파악해야 하는데, 무자비하게 예외를 던질 수 있게 예외 명세를 정의하므로, 좀 불편한 코드가 되고야 만다.  예외 명세를 안쓰는게 쓰는것보다 더 좋다고 배운 나로써로는, 이런 문제는 예외 명세를 사용하지 않음으로써 쉽게 피해 갈 수 있다.

참, 그리고 위의 코드에서, Base의 복사 배정 연산자가 const Derived& 를 받는데, 예를 만들기 위해서 이니, 절대 하지 말아야 할 코드이다. 소멸자도 마찬가지...

어떻게 해서 저렇게 무자비하게 예외를 상속시키는 코드를 만들어 주는가?


이 코드가 던지지 말아야 할 예외도 던질 수 있게 되기 때문에, 모두 던질 수 있게 만들어 주는 것이다.


암묵적으로 생성되는 각 함수들의 문제점은 무엇인가?

문제점은 위에서 정리한 개념의 문제가 있다. 다른 관점에서의 문제점으로는 경우에 따라 암묵적으로 생성해 주지 않거나 다른 서명으로 만들어 준다. 이것 때문에, 컴파일이 되지 않은 경우가 있지만, 컴파일 타임 에러를 벹어 내기 때문에, 문제는 되지 않는다.

생성 될 경우엔, 공통적으로 public, inline 으로 만들어 지며, 예외 명세는 모두 상속 된 형태를 사용 하게 된다.

2 ) 다음의 클래스 X에 대해 암묵적으로 선언, 정의되는 함수들은 무엇이며, 그 함수들의 서명(signature)은 어떻게 되는가?

이 질문은 위에서 지적했던 "경우에 따라서, 암묵적으로 생성해 주지 않거나 다른 서명으로 만들어 준다"를 좀 깊게 파는 질문이다. 코드를 보자.


다음 처럼 될 것이다.


여기서 왜 const X& 로 서명 되지 않고, X& 로 서명 되었을까?

왜냐하면 std::auto_ptr 의 복사 생성자와 operator= 이 T& 로 되어 있기 때문이다. 이것은 X의 멤버 변수나 상속 클래스의 복사 생성자, operator= 에 의하여, 영향을 받는 다는 것을 알 수 있는 근거이다.

최우선으로 const T& 로 생성 되나, 안될 경우 T& 로 된다는 것을 알아야 한다.


3 ) 암묵적으로 생성되는 함수가 없는 기반 클래스가 있다. 이 기반 클래스를 상속하는 파생 클래스는 기본 생성자, 복사 생성자, 복사 할당자, 소멸자를 호출 할 때, 어떠한 점을 지켜야 한다고, 주석으로 명시해 놓았다.

그런데 파생 클래스 작성자는 이 주석을 보지 않고, 암묵적으로 작성된 함수들을 사용하다, 문제점이 생겼다. 이 문제점 때문에,  파생 클래스가 암묵적으로 생성된 함수들을 사용하지 못하게, 컴파일 타임에 오류를 발생 시키거나, 아무리 못해도, 실햄시점 오류를 발생 할 수 있을까?

컴파일 타임에 오류를 발생 시키는 방법으로는 기반 클래스의 "복사 생성자, 복사 할당자"를 private 로 만들어 버리면, 파생 클래스에선 반드시 "파생클래스용 복사 생성자, 복사 할당자"를 선언 해야만 한다. 왜냐하면, 기반 클래스의 복사 생성자와 복사 할당자가 private 이라면, 파생 클래스에선 복사 생성자와 복사 할당자를 정의해 주지 않는 규칙이 있기 때문이다.

여기서 부수적인 효과로 기반 클래스의 기본 생성자는 정의되지 않는데, 복사 생성자 라는 "생성자"가 단 1개라도 선언되어 있으면, 기본 생성자는 만들어 주지 않는 규칙 때문이다.

이것으로 3가지 함수는 손 쉽게 컴파일 타임에 에러를 벹어내게 바꾸었는데, 소멸자는 항상 단 한개 밖에 가지지 못하기 때문에, 그리 문제가 될 경우는 없다고 본다.(똥누고 똥꼬 안닦으면, 자기에게 똥 냄새가 날 뿐이다.)

이것 외에 다른 방법이 존재하긴 하나, 이 방법이 제일 좋으니 이것만 알아도 된다.(다른것은 구조가 좀 복잡해 지는 경우가 있다.)


총평

일전에 보았던 Effective C++ 에서 http://ikpil.tistory.com/406, http://ikpil.tistory.com/407 에 조심하라는 취지해서 이야기 했던 것이 기억이 나는데, 여기서 좀 더 실용적이고 구체적으로 정리가 된 것을 볼 수 있어서 좋았다.


posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요