2017-01-16 3 views
1

저는 C++에 비교적 익숙하지 않습니다. 이제는 다운 캐스팅을 피할 수없는 디자인의 한 부분에 직면 해 있습니다. 나는 이것이 일반적으로 나쁜 디자인의 징표라는 것을 알고 있습니다. 그래서 이것을하기위한 더 좋은 방법이 무엇인지 알고 싶습니다. 나는 동적 특성을 다루는 몇 가지 기능을 추가하는 새로운 Frame 서브 클래스, MechanicalFrame를 만들기 위해 지금 할상속 된 트리 클래스에서 다운 캐스팅을 피하십시오

class Frame 
{ 
    private: 
     Frame *_parent; 
     std::vector<Frame*> _children; 

    public: 
     Frame* getParent() const; 
     std::vector<Frame*> getChildren() const; 
     ... (extra methods for geometrical transformations) 
} 

:

나는 그들 사이에 기하학적 변환을 기하학적 프레임 나무를 나타내며 할 수있는 클래스 Frame 있습니다.

class MechanicalFrame 
{ 
    private: 
    double mass; 
    ... 
    public: 
    void compute(); 
} 

내 문제는 "컴퓨팅"방법은 몇 가지 재귀 논리를 구현해야한다는 것입니다, 그래서는 다음과 같이 포함됩니다 : getChildrenFrame* 및하지의 vector를 반환하기 때문에, 그러나

MechanicalFrame::compute() 
{ 
    for element in getChildren(): 
    element.compute(); 
} 

MechanicalFrame*,이 시점에서 static_cast을 작성해야합니다. 나는 문제를 생각을 많이 부여했지만, 내가 찾은 솔루션 아무도 나에게 완벽하게 만족되지 않습니다 :

해결 방법 1) 정적 캐스트 : 어떻게 든 나쁜 디자인

솔루션 2)를 추가 표시 (Frame)을 더미 구현으로, 즉 예외를 던지면서 메소드를 계산할 때 파생 클래스를 기반으로 부모 클래스의 구현을 강제하는 것은 부 자연스럽게 보입니다.

해결 방법 3) Frame에서 전체적으로 MechanicalFrame으로 분할하십시오. 이는 이미 Frame에서 사용할 수있는 많은 기능을 다시 구현해야 함을 의미합니다.

도움이 될 것입니다. 사전에 많은 감사합니다 :)

+0

기본 클래스에'Frame :: compute()'를 만들고 가상으로 만듭니다. 질문은 - 기본 클래스를 인스턴스화 할 예정입니까? 아마도'Frame'을 추상 클래스로 만드는 것이 좋은 생각 일 것입니다. – pSoLT

+0

'MechanicalFrame :: getChildren()'에있는'Frame *'포인터가 모두'MechanicalFrame' 인스턴스를 가리키고 있다는 것을 확신한다면'static_cast'에 아무런 문제가 없습니다. 디버그 빌드 **에서'dynamic_cast' +'assert'를 사용하여 실수를 잡으십시오. –

+0

@VittorioRomeo 정말 못 생겼습니다. – pSoLT

답변

1

당신이 MechanicalFrame::getChildren()의 모든 Frame* 포인터가 MechanicalFrame 인스턴스를 가리키는 것이 확실한 경우에, 나는 static_cast 어떤 문제가 표시되지 않습니다. 실수를 잡으려면을 번으로 번으로 + assert번으로 디버그 빌드를 사용하십시오.

template <typename TOut, typename T> 
auto downcast(T* ptr) 
{ 
    static_assert(std::is_base_of<T, TOut>{}); 

    assert(ptr != nullptr); 
    assert(dynamic_cast<TOut>(ptr) == ptr); 

    return static_cast<TOut>(ptr); 
} 

성능이 있다고


고지 (. downcast의보다 철저한 구현에 대한 나의 Meeting C++ 2015 lightning talk "Meaningful casts" 또는 내 current implementation in vrm_core 참조) downcast이 같은입니다

void MechanicalFrame::compute() 
{ 
    for(auto frame_ptr : getChildren()) 
    { 
     downcast<MechanicalFrame*>(frame_ptr)->compute(); 
    } 
} 

여기서 이점을 피하십시오. virtual dispat ch. 생성 된 어셈블리의 차이점을 확인하려면 this snippet on gcc.godbolt.org으로 재생하십시오.> 기본 클래스 - -> 파생 클래스)

class IFrame 
{ 
public: 
    virtual void compute()=0; 
} 

class Frame:public IFrame 
{ 
public: 
    virtual void compute() {/*nothing to do*/} 
} 

class MechanicalFrame:public Frame 
{ 
public: 
    virtual void compute() {/*your implementation with mass*/} 
} 
2

사용 다형성 (polymorphic), 당신은 패턴 아래에 따를 수

솔루션 2) (인터페이스를 사용 1-3 번 해결책에서 여러분이 작성한 것에 대해서는 자세히 설명하지 않습니다.

이 같은, 그 MechanicalFrame 클래스의 children 다른 모든 클래스를 분할의 MechanicalFrame 클래스에 추가 기능을 추가 할 수 :

void MechanicalFrame::compute() { 
    ... 
    for (auto* child : getMechanicalChildren()) { 
     child->compute(); 
    } 
} 
:

class Frame { 
public: 
    std::vector<Frame*> getChildren(); // returns children 
    void addChild(Frame* child);  // adds child to children 
private: 
    std::vector<Frame*> children; 
} 

class MechanicalFrame : public Frame { 
public: 
    void compute(); 
    std::vector<MechanicalFrame*> getMechanicalChildren(); // returns mechanical_children 
    void addChild(MechanicalFrame* child);     // adds child to mechanical_children 
private: 
    std::vector<MechanicalFrame*> mechanical_children; 
} 

compute의 하나의 가능한 구현은 다음과 같다

UP : 내가 아는 한, 캐스트의 문제점 중 하나는 코드가 매우 다른 방식으로 동작하기 시작한다는 것입니다 객체의 실제 클래스에 추가하고 부모 클래스 객체를 자식 클래스로 대체 할 수 없습니다 (Liskov principle 참조). 이 대답에 설명 된 접근 방식은 실제로 메서드에서 무시되는 방식으로 MechanicalFrame 자식을 추가 할 수 있도록 Frame의 "메커니즘"사용 원칙을 변경합니다. 그런 다음 클라이언트 코드가 될 것입니다

class Frame; 
class MechanicalFrame; 

class FrameVisitor 
{ 
public: 
    virtual ~FrameVisitor() = default; 

    virtual void visit(Frame&) = 0; 

    virtual void visit(MechanicalFrame&) = 0; 
}; 

class Frame 
{ 
public: 
    virtual void accept(FrameVisitor& visitor) 
    { 
     visitor.visit(*this); 
    } 

    void acceptRecursive(FrameVisitor& visitor) 
    { 
     accept(visitor); 

     for (Frame* child : getChildren()) 
     { 
      child->acceptRecursive(visitor); 
     } 
    } 

    ... 
}; 

class MechanicalFrame : public Frame 
{ 
public: 
    virtual void accept(FrameVisitor& visitor) override 
    { 
     visitor.visit(*this); 
    } 

    ... 
}; 

:

+1

그건 내가 코멘트에 쓴 것입니다. – pSoLT

-1

새로운 아이디어를 요구하고 있기 때문에, 내가

0

또 다른 옵션은 Visitor 패턴을 사용하는 것입니다 일반적으로

class ConcreteVisitor : public FrameVisitor 
{ 
public: 
    virtual void visit(Frame& frame) override 
    { 
     // Deal with Frame (not a subclass) object. 
    } 

    virtual void visit(MechanicalFrame& frame) override 
    { 
     // Deal with MechanicalFrame object. 
    } 
}; 

Frame root = ...; 
ConcreteVisitor visitor; 
root.acceptRecursive(visitor); 

, 방문자 패턴을 사용하면 이기종 객체의 계층 구조를 탐색 할 수 있으며 타입 캐스팅없이 연산을 수행합니다. 유형 계층 구조가 다소 안정적 일 때 조작 수가 늘어날 것으로 예상되는 경우에 가장 유용합니다.

관련 문제