2012-03-28 2 views
7

가상 소멸자가없는 클래스로부터 상속해서는 안된다는 말을 항상 들었습니다. 상속을 자주 사용하지 않기 때문에 많은주의를 기울이지 않았습니다. 이 규칙은 다형성을 사용하고 싶지 않지만 모든 클래스 기능을 원한다면 더 적용할까요? 구체적으로 말하자면, 다형 적으로 사용하지 않는 한, 잘 정의 된 행동으로 다음 클래스가 안전할까요? (파생 개체에 즉 더 삭제베이스 포인터 없음)가상 소멸자가없는 클래스로부터 상속 받기

template<typename T> 
class SomewhatSafeVector : public std::vector<T> 
{ 
public: 
    typedef std::vector<T> base; 

    T& operator[](unsigned n) { 
     if (n >= base::size()) 
     { 
      throw IndexOutOfBounds(); 
     } 
     return base::operator[](n); 
    } 
}; 
+1

괜찮은지 여부는 신경 쓰지 않아도되지만 표준 라이브러리 컨테이너에서 파생해서는 안됩니다. 또한 범위 내에서 동적 컨테이너에 액세스하는 데 문제가 있으면 큰 그림 알고리즘 사고 ("0-1- 많은"및 "범위")를 고려하는 것이 좋습니다. 범위를 벗어난 액세스는 보통 논리 * 오류. –

+4

상속은 구현 재사용이 아니라 인터페이스 재사용을 의미하기 때문에 특정 사례에서 상속은 굉장히 우아한 해결책이라고 생각합니다. 'operator []'는'std :: vector'가 예외를 throw하지 않기 때문에 인터페이스를 재사용하지 않아도됩니다. 코드를 재사용하고 싶다면 일반 공유 함수를 사용하거나 (이 경우처럼)'std :: vector'를'SomewhatSafeVector'의 멤버로 만듭니다. –

+0

@KerrekSB : 첫 번째 이유는 무엇입니까? 두 번째로, 나는 그런 문제가 없다. 하지만 경계 체크 컨테이너는 교육 및 디버깅 목적에 좋은 아이디어라고 생각합니다. –

답변

6

나는 항상 당신이 가상 소멸자

이 설명하는 모든 복잡한 너무 많은 시간을 소요하고 있기 때문에 초보자에게 주어진 엄지 손가락의 규칙입니다없는 클래스에서 상속 안 들었어요 (실제로 운동 프로그램에 비용이 많이 드는 것은 아니지만) 실제로는 항상 안전하다고 생각합니다.

기본 클래스의 소멸자 virtual없이 완벽하게 상속을 사용할 수 있습니다. 반면에 기본 클래스에 전혀 virtual 메서드가 없으면 상속은 작업에 잘못된 도구 일 것입니다. 예를 들어 을 사용하는 경우 SafeVector<T> sv; sv[3];을 입력하면 안전합니다. 그러나 std::vector<T>& v = sv; v[3];을 입력하면 ... 이 숨어 있으므로 기본 클래스 메소드가 숨겨져 있기 때문에 무시할 수 있습니다 레벨, 그들은 당신을 알릴 것이다).

올바른 방법은 composition을 사용하고 실제로 사용하는 메소드의 구현 멤버에게 전달 메소드를 만드는 것입니다. 실제로 많은 사람들이 상속받을 수있는 위임 (using attribute.insert;)을 지원하지 않기 때문에 실제로 피곤해집니다.

또 다른 대안은 자유 방법을 제한없이 자유롭게 추가 할 수 있으므로 무료 메서드로 안전한 메서드를 제공하는 것입니다. "OO"사고 방식을 가진 사람들에게는 덜 관용적 일 수 있으며 일부 운영자는 그렇게 추가 될 수 없습니다.

5

당신이 클래스 다형 (파생 개체없이 삭제베이스 포인터)를 사용하지 않을 경우, 그것은 정의되지 않은 행동이 아니다.

참조 :

C++ 03 표준 : 5.3.5

5.3.5/1 삭제 :

h 제 표현 연산자가 가장 파생 된 개체를 파괴 (1.8) 또는 new-expression에 의해 생성 된 배열.
삭제 표현 :
:: 캐스트 표현을 삭제 선택
:: 선택 삭제 [] 캐스트 표현

5.3.5/3 : 첫 번째에서

대체 (객체 삭제), 피연산자의 정적 유형이 동적 유형과 다른 경우 정적 유형은 피연산자의 동적 유형의 기본 클래스가되어야하며 정적 유형은 가상 소멸자를 가져야하거나 동작이 정의되지 않습니다. 두 번째 대안으로 (배열 삭제) 개체의 동적 유형은 정적 유형에서 다릅니다을 삭제하는 경우는, 동작은 그냥 할 수있는) 당신은 다형 객체를 사용하실 수 있습니다

4

undefined.73입니다 'delete'이 다형성으로 나타납니다. std::vector<>*을 통해 클래스 개체에 대한 포인터를 삭제하지 않으면 안전합니다. 이외에도

: 당신은 당신의 operator[] 따라서 단순화 수 있습니다 당신은 (즉, 결코 업 캐스팅 참조 또는 포인터) 다형성을 사용하지 않을 경우

T& operator[](unsigned n) { return this->at(n); } 
2

예, 안전하지 않은 파괴를 수행 할 수있는 방법이 없습니다 .

Mixin 클래스는 종종 이런 식으로 사용되고 CRTP는 몇 가지 패턴을 명명하기 위해 거의 가상 소멸자를 의미합니다.