2012-05-08 5 views
4

나는 순수 가상 함수를 통해 그 서브 클래스에 파생 클래스에 정적 멤버를 적용하는 방법?

Primitive

일부 기능을 적용하는 등 나는 여러 가지 다른 classes-- Sphere, Plane을 도출되는 기본 클래스, Primitive,,, 예컨대 intersect()을 보유하고 있습니다. intersect의 계산은 인스턴스 데이터에 따라 다르므로 멤버 메서드로 사용하는 것이 좋습니다.

내 문제는 다음에서 발생합니다. 모든 파생 된 인스턴스가 std::string type() 멤버 메소드를 통해 유형을 식별 할 수있게하려고합니다. 동일한 클래스의 모든 인스턴스가 동일한 유형을 반환하므로 type()static 메소드로 만드는 것이 좋습니다. 또한 모든 Primitive 하위 클래스에서이 메서드를 구현하기를 원하기 때문에 위의 intersect()과 같은 순수 가상 함수로 만들고 싶습니다.

그러나 C++에서는 정적 가상 메서드가 허용되지 않습니다. C++ static virtual members?Can we have a virtual static method ? (c++) 비슷한 질문을하지만 파생 된 클래스에 함수를 적용해야한다는 요구 사항은 없습니다.

위와 관련하여 도움을받을 수있는 사람이 있습니까?

+1

다형성을 원하는 경우 왜 정적 유형을 원하겠습니까? 인터페이스에서 기본 클래스 포인터를 사용하고 런타임에 유형을 동적으로 파악하도록하십시오. 가상 메서드에 대한 전체적인 점이 있습니다. – AJG85

+0

어떻게 가상 정적이라고 부릅니까? –

+0

[http://stackoverflow.com/questions/325555/c-static-member-method-call-on-class-instance] 또는 'this' 포인터를 통해서도 인스턴스를 호출 할 생각이었습니다. , 예. [http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr039.htm] – wsaleem

답변

0

파생 클래스마다 적절히 구현 된 정적 인 가상 메서드를 정적 메서드를 호출하거나 정적 문자열을 반환 할 수 있습니다. 문자열이 정적을 위해

#include <iostream> 
#include <string> 

struct IShape { 
    virtual const std::string& type() const =0; 
}; 

struct Square : virtual public IShape { 
    virtual const std::string& type() const { return type_; } 
    static std::string type_; 
}; 
std::string Square::type_("square"); 

int main() { 

    IShape* shape = new Square; 
    std::cout << shape->type() << "\n"; 

} 

어쨌든 모든 서브 클래스에 대한 type() 메소드를 구현해야합니다 주, 그래서 당신이 할 수있는 최선이다. 그러나 문자열 대신 enum을 사용하는 것이 좋습니다. 코드에서 불필요한 문자열 비교를 피하십시오.

이제 문제의 기본 사항으로 돌아 가면 디자인에 다소 결함이있는 것 같습니다. 같은 유형의 모양 (교차하는 두 평면은 비행기, 선 또는 교차하지 않을 수 있음) 에서조차 교차로 인해 발생하는 모양 유형이 크게 달라지기 때문에 모든 종류의 모양에서 작동하는 일반 교차 기능을 실제로 사용할 수 없습니다. 모두 예를 들어). 따라서 일반적인 솔루션을 제공하려 할 때 이러한 곳에서 유형 검사를 수행하게 될 것입니다. 그러면 더 많은 모양을 추가 할 수있게 될 것입니다.

+0

고마워, 나는 가상 메서드를 통해 정적 문자열을 반환하는 아이디어를 좋아한다. 하지만 모든 하위 클래스에는'static std :: string myType' 선언이 있습니다. 이것은 코드 중복처럼 보입니다. 그것을 피하기 위해 상속을 사용할 수 없습니까? – wsaleem

+0

불행히도 각 하위 클래스의 가상 메서드에서 벗어날 수는 없습니다. 나는 더 많은 정보와 코멘트를 추가했다. 그것은 까다로운 문제입니다 ... – juanchopanza

+0

@ juanchopanza 당신은 내 대답을 볼 수 있습니다. –

2

제공된 링크에서 논의한 이유로 인해 가상 멤버를 정적으로 만들 수 없습니다.

파생 클래스에서 함수를 적용해야한다는 요구 사항에 대한 질문은 파생 클래스가 함수를 구현해야하는 추상 기본 클래스에서 함수를 순수 가상으로 만들어 처리됩니다.

4

잠시 생각해 봅시다. 나는 당신이 2 개의 서브 클래스를 가지고있을뿐만 아니라, 이것을 일반화하자.

가장 먼저 떠오르는 것은 코드 복제, 확장 성 및 친밀 성입니다. 다음을 확장 해 봅시다 :

더 많은 클래스를 추가하려면 가능한 한 최소한의 코드를 변경해야합니다.

intersect 작업이 교환 법칙이 성립, 자신이 밖으로 질문 클래스 내부의 논리를 BA 교차의 코드와 같은 장소에 있어야 AB 교차, 그래서 유지하기위한 코드이기 때문입니다.

또한 새로운 클래스를 추가한다고해서 기존 클래스를 수정해야하는 것이 아니라 위임 클래스를 확장해야한다는 것을 의미합니다 (예, 여기에 패턴이 있습니다).

이 현재 구조, 나는 가정 (또는 intersect 비슷한, 아마 반환 유형,하지만 지금은 중요하지 않음) :

struct Primitive 
{ 
    virtual void intersect(Primitive* other) = 0; 
}; 
struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
}; 
struct Plane : Primitive 
{ 
    virtual void intersect(Primitive* other); 
}; 

우리는 이미 우리가 Plane 내부의 교차 로직을하지 않으 결정 또는 Sphere, 그래서 우리는 새로운 class를 만들 :

struct Intersect 
{ 
    static void intersect(const Sphere&, const Plane&); 
    //this again with the parameters inversed, which just takes this 
    static void intersect(const Sphere&, const Sphere&); 
    static void intersect(const Plane&, const Plane&); 
}; 

이 당신이 새로운 기능을 추가 할 예정 클래스, 새로운 논리이다. 예를 들어 Line 클래스를 추가하려는 경우 intersec(const Line&,...) 메서드를 추가하기 만하면됩니다.

새 클래스를 추가 할 때 기존 코드를 변경하고 싶지 않습니다. 따라서 교차 함수 내부의 유형을 확인할 수 없습니다.

우리는 종류에 따라 다르게 작동이 (전략 패턴)을위한 행동 클래스를 만들 수 있습니다, 우리는 나중에 확장 할 수

struct IntersectBehavior 
{ 
    Primitive* object; 
    virtual void doIntersect(Primitive* other) = 0; 
}; 
struct SphereIntersectBehavior : IntersectBehavior 
{ 
    virtual void doIntersect(Primitive* other) 
    { 
     //we already know object is a Sphere 
     Sphere& obj1 = (Sphere&)*object; 
     if (dynamic_cast<Sphere*>(other)) 
      return Intersect::intersect(obj1, (Sphere&) *other); 
     if (dynamic_cast<Plane*>(other)) 
      return Intersect::intersect(obj1, (Plane&) *other); 

     //finally, if no conditions were met, call intersect on other 
     return other->intersect(object); 
    } 
}; 

그리고 우리의 원래의 방법에

우리는 거라고 :

struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
    { 
     IntersectBehavior* intersectBehavior = BehaviorFactory::getBehavior(this); 
     return intersectBehavior.doIntersect(other); 
    } 
}; 
:
struct Sphere : Primitive 
{ 
    virtual void intersect(Primitive* other) 
    { 
     SphereIntersectBehavior intersectBehavior; 
     return intersectBehavior.doIntersect(other); 
    } 
}; 

이보다 청소기 디자인은 동작 중 추상적 실제 유형, 팩토리를 구현하는 것

그리고 심지어 모든 클래스에 대해 이렇게하기 때문에 intersect이 가상 일 필요는 없습니다. 새로운 클래스를 추가 할 때이 디자인

  • 에게 기존 코드를 수정할 필요를 따라하지 않는 경우

  • 는 각각의 새로운 유형
  • IntersectBehavior을 확장 한 곳에서 구현이 새로운 유형의 경우 Intersect 클래스에서 구현을 제공하십시오.

그리고 이것이 더 완벽해질 수있을 것입니다.

+0

자세한 답변을 보내 주셔서 감사합니다. 나는 원래의 게시물에서 표현의 경제가 당신을 잘못 이끌었다 고 생각합니다. 'intersect()'는'Prim Ray'를'Primitive' 인스턴스 (포인터)가 아닌 인수로 취합니다. 각'Primitive' 클래스는'Ray'와 어떻게 교차하는지 알고 있습니다. 외부로'intersect '를 이동 시키려면 커스텀'Intersect' 클래스가 캡슐화를 위반하는'Primitive' 내부에 접근해야합니다. – wsaleem

+0

타입 확인은 결국 동적 수집 (dynamic_cast)으로 이어지며, 일반적으로 수집 된 사용법은 권장되지 않습니다. 어떤 전망? – wsaleem

+0

@wsaleem 잘, netiher는'id' 회원을 가지고 있습니다. 그러나 'dynamic_cast'가 이미 존재합니다. 왜 바퀴를 재발 명합니까? –

1

동일한 클래스의 모든 인스턴스가 동일한 유형을 반환하므로 type()을 정적 메소드로 지정하는 것이 좋습니다.

아니요. 함수를 호출하기 위해 객체의 인스턴스가 필요하지 않을 때 정적 메서드를 사용합니다.이 경우 객체의 유형을 식별하려고 시도하므로 을 수행하면에 인스턴스가 필요합니다.

어쨌든 모든 메서드 본문은 모든 개체에서 공유되므로 복제에 대해 걱정할 필요가 없습니다. 한 가지 예외는 함수가 인라인 인 경우이지만 컴파일러는 오버 헤드를 최소화하기 위해 최선을 다할 것이며 비용이 너무 높으면 비 인라인으로 설정할 수 있습니다.

P. 클래스 계층 구조 외부에서 자신을 식별하도록 클래스에 요구하면 대개 잘못된 코드의 냄새가 난다. 다른 방법을 찾아보십시오.

+0

''인스턴스를 필요로하지 않을 때 정적 메서드를 사용하므로 인스턴스가 필요합니다. ''고마워요, 그건 사실입니다. 나는 그걸 생각하지 않았다. ''모든 방법 본체는 모든 객체에 의해 공유되므로 복제에 대해 걱정할 필요가 없습니다. ''또 다른 좋은 지적입니다. – wsaleem

+0

당신과 @ ajg85 모두 내 디자인이 궁금합니다. 좀 더 자세히 설명해 드리면 더 나은 대안을 제안 할 수 있습니다. 'Primitive *'객체를 통해'Primitive :: emitXml()'메소드를 호출하고 싶다. 방출 된 XML은 다음을 포함합니다. ' ...', 포인터가 가리키는'Primitive' 객체의 타입에 따라 다르다. EmitXml() 함수의'type' 속성 값을 채우기 위해'type()'함수가 필요합니다. – wsaleem

+0

@wsaleem, 실제로 합법적 인 사용 사례입니다. 객체 유형에 따라 다른 동작을 얻기 위해'if' 문에서이 문법을 사용하는 것이 많은데, 그 코드는 내가 언급 한 코드 냄새입니다. –

관련 문제