2010-03-09 1 views
12

나는 클래스가 서로에 따라 교차하는 매우 복잡한 클래스 계층 구조를 가지고있다 : C와 A의 인스턴스를 각각 반환하는 메서드를 포함하는 두 개의 추상 클래스 A와 C가있다. 상속 된 클래스에서 나는 상속 관계 선을 앞으로 선언하는 방법을 모르기 때문에이 경우에는 문제가되는 공동 변형 유형을 사용하려고합니다.C++ : 캐스팅하지 않고 상속 된 클래스에서 "잘못된 공변 반환 유형"을 방지하려면 어떻게해야합니까?

컴파일러가 D가 C의 하위 클래스라는 것을 알지 못하므로 'test.cpp : 22 : error :'virtual D * B :: outC() ' "오류에 대한 공변 반환 유형이 잘못되었습니다.

class C; 

class A { 
public: 
     virtual C* outC() = 0; 
}; 

class C { 
public: 
     virtual A* outA() = 0; 
}; 


class D; 

class B : public A { 
public: 
     D* outC(); 
}; 

class D : public C { 
public: 
     B* outA(); 
}; 

D* B::outC() { 
     return new D(); 
} 

B* D::outA() { 
     return new B(); 
} 

B :: outC()의 반환 유형을 C *로 변경하면 예제가 컴파일됩니다. 상속 된 클래스에서 B * 및 D *를 반환 유형으로 유지할 수있는 방법이 있습니까? (방법이 있다는 것이 내게 직관적일까요?)

+2

정말 그 유형이 얽히고 필요합니까? (커플 링이라고하는 것은 조금 짧을 수도 있습니다.) –

+0

때로는 언어의 문제가 아니기 때문에 우리가 사용하려고 시도하는 방식에 문제가 있습니다. 두 계층 구조가 너무 깊숙이 결합되어 있다면, 어쨌든 서로 없이는 일할 수없는 것처럼 보일 수 있으므로 하나의 계층 구조 (A와 C, B와 D)를 결합하는 것이 더 나을 것입니다. . –

+0

글쎄, 나는 두 종류의 클래스가있다 : 태스크에 대한 스펙과 실제로 태스크를 실행하는 executor (여러 개의 스레드를 시작 함). 스펙 자체는 태스크의 팩토리이어야하며 모든 실행 프로그램은 스펙에 액세스해야합니다. 그리고 그들의 작업에 다양한 사양이 있습니다. 따라서 집행자는 사양을 래핑하므로 커플 링은 한 방향으로 만 강하고 다른 방향으로는 단지 공장 방법입니다. – Searles

답변

7

나는 C++에서 직접 결합 된 공변량 멤버를 가지고있을 방법이 없다. 레이어를 추가하거나 공변 반환을 구현해야합니다. 이 두 번째 옵션합니다 (static_cast가 유효한지 몇 가지 정적 검사와 함께) 컴파일러에 의해 암시 적으로 수행되는 정도라는 첫 번째 옵션

class C; 

class A { 
public: 
     virtual C* outC() = 0; 
}; 

class C { 
public: 
     virtual A* outA() = 0; 
}; 


class BI : public A { 
public: 
}; 

class D : public C { 
public: 
     BI* outA(); 
}; 

class B: public BI { 
public: 
     D* outC(); 
}; 

D* B::outC() { 
     return new D(); 
} 

BI* D::outA() { 
     return new B(); 
} 

들어 두 번째

class C; 

class A { 
public: 
     C* outC() { return do_outC(); } 
     virtual C* do_outC() = 0; 
}; 

class C { 
public: 
     virtual A* outA() = 0; 
}; 


class D; 

class B : public A { 
public: 
     D* outC(); 
     virtual C* do_outC(); 
}; 

class D : public C { 
public: 
     B* outA(); 
}; 

D* B::outC() { 
     return static_cast<D*>(do_outC()); 
} 

C* B::do_outC() { 
     return new D(); 
} 

B* D::outA() { 
     return new B(); 
} 

참고

.

0

클라이언트 쪽 기대로 인해이 작업을 수행 할 수 없습니다. C 인스턴스를 사용할 때 어떤 C (D 또는 다른 것)인지 알 수 없습니다. 따라서 B 포인터 (파생 된 클래스에 대한 호출의 결과이지만 컴파일 타임에 그것을 알지 못함)를 A 포인터에 저장하면 모든 메모리가 올바르게 될지 확신 할 수 없습니다.

다형성 유형에서 메소드를 호출하면 런타임 환경에서 객체의 동적 유형을 확인하고 클래스 계층에 맞게 포인터를 이동해야합니다. 나는 당신이 공분산에 의지해야한다고 확신하지 못합니다. 한번 보자. this

+1

당신은 오해했습니다. 공변 리턴 유형은 * 허용됩니다 *. 클라이언트가'A' 포인터를 기대하지만'B' 포인터를 주면'B' *의 모든 인스턴스가'A'의 * 인스턴스이기도합니다. 이 경우의 문제는 컴파일러가'B :: outC'의 선언에서 새로운 반환 값 타입'D'가 원래의 반환 값 타입'C'의 자손임을 검증 할 수 없다는 것입니다. 컴파일러가'D'의 완전한 정의를 볼 수 있다면, 새로운 서명을 허용했을 것입니다. 컴파일러는'D :: outA'에 대한 불만을 제기하지 않습니다. 왜냐하면'C'가'A'의 자손임을 알고 있기 때문입니다. –

+1

예에서 클래스 D의 공동 변형 반환 유형은 문제가되지 않습니다. – Searles

4

내가 아는 한 명시 적 전송 없이는이 작업을 수행 할 수있는 방법이 없습니다. 문제는 클래스 D의 전체 정의를 볼 때까지 클래스 B의 정의 DC의 하위 클래스임을 알 수 없지만, 그것을 볼 때까지 클래스 D의 정의 BA의 하위 클래스임을 알 수 없다는 것입니다 클래스 B의 완전한 정의. 따라서 순환 종속성이 있습니다. 유감스럽게도 forward 선언은 상속 관계를 지정할 수 없기 때문에 forward-declarations로는 해결할 수 없습니다.

which I found can be solved 템플릿을 사용하여 공변수 clone() 메서드를 구현하는 데 비슷한 문제가 있지만 순환 참조를 해결할 수 없기 때문에 유사한 솔루션이 여전히 실패합니다.

+0

이것은 실제로 순환 종속성의 문제입니다. cross-dependent typedef, enums, fields 등을 사용할 때도 마찬가지입니다. +1 – doc

+0

모두가이 '복제본'문제에 물 렸습니다 ... 해결책에 대해 마음에 들지 않는 부분은 포장 파트를 거칠게 표현했습니다. 'typedef'는 앞으로 선언 될 수 없으며 클라이언트가 장식되지 않은 이름을 사용하지 않는 것을 알아야하는 경우 고통이됩니다. / –

관련 문제