2013-10-05 9 views
2

내가 C++에 비교적 새로운 해요, 나는이 목록 반복에 문제가있어 C#을 배경에서 오는 :순회 서브 클래스 오류의 목록

나는 개체 및 호출 목록을 통해 루프 하나 개의 방법이 각각을위한 업데이트 방법으로 위대한 작품입니다. 목록의 유형은 std::list<EngineComponent>이며 engineComponents입니다.

void Game::Update() 
{ 
    for (EngineComponent component: this->engineComponents) 
    { 
     component.Update(); 
    } 
} 

는 또한 EngineComponent의 서브 클래스 DrawableEngineComponent라고합니다.

void Game::Draw() 
{ 
    for (DrawableEngineComponent component: this->engineComponents) 
    { 
     component.Draw(); 
    } 
} 

이 " 'DrawableEngineComponent'에서 'EngineComponent'존재로부터 적절한 사용자 정의 변환"오류를 생성합니다 나는 비슷한 반복을 할 때

문제가 나타납니다. 이 구현이 C#에서 모두 훌륭하고 멋쟁이 였다는 것을 감안할 때 C++에서이 문제를 해결하는 최선의 방법이 무엇인지 잘 모르겠습니다.

내가 할 수있는 몇 가지 대안 방법을 생각할 수 있지만 수동으로 변환을 정의하지 않고도 C#과 유사한 방식으로 C++에 기능이 있는지 궁금합니다. 당신이 그 오류)

+1

캐스팅 시도가 잠재적 인 슬라이싱 문제를 고함입니다. , 당신은 그것에 대해 더 자세히 알고 싶으면 [이 질문과 답변을 읽으십시오] (http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c/274634#274634)를 잘 할 수 있습니다 – WhozCraig

+1

당신은 C#에서이 함수가 작동한다고 말하지만 그것이 사실 인지는 잘 모르겠습니다 :-) C#에서는 목록의 구성 요소 중 하나가 * Drawable이 아닌 경우 어떻게됩니까? 그것은 추락합니까? 또는 목록에서 해당 항목을 건너 뛸 수 있습니까? 이와 관련하여 당신이 기대하거나 바라는 행동을 명확히하십시오. –

+0

@AaronMcDaid 네 말이 맞아. 그렇지 않아. 내가 이것을 썼을 때 나는 약간 혼란스러워했다. 그것은 C#에서 매우 쉽게 할 수 있습니다. 각 객체가 루프에 올 때 (한 줄의 코드로) 검사하면 내가 작성해야만했던 것입니다. 좋은 자리 :) –

답변

2

실제 이유 방법이다;

class EngineComponent 
{ 
public: 
    EngineComponent(void); 
    ~EngineComponent(void); 

    virtual void Update(void); 
}; 


class DrawableEngineComponent : public EngineComponent 
{ 
public: 
    DrawableEngineComponent(void); 
    ~DrawableEngineComponent(void); 

    virtual void Draw(void); 
}; 

그리고 그래, 내가 XNA 프레임 워크를 조금 복사하고는 다음과 같이 관련된 두 클래스에 대한

정의는 당신은 정의 된 범위 기반, 오히려 기준보다 복사하여 객체를 검색하는 경우 :

for (EngineComponent component: this->engineComponents) 
{ 
    // component is a copy of the object in the list 
} 

EngineComponent는 슈퍼 클래스이며, 그래서 나는 없다 파생 된 클래스에 캐스트를 던지십시오. EngineComponent 목록에서 DrawableEngineComponent을 복사하려고하면 컴파일러에서 원본 개체가 실제로 파생 클래스인지 알 수 없습니다.

표준 컨테이너는 실제로 다형성 객체를 잘 처리하지 않습니다. 더 나은 해결책은 객체에 대한 포인터를 저장하는 데 std::shared_ptr을 사용하는 것입니다.

std::list<std::shared_ptr<EngineComponent>> myList; 
myList.push_back(std::make_shared<DrawableEngineComponent>()); 

이 공유 포인터에 DrawableEngineComponent을 포장하고 목록에 저장합니다. 원래 방법과 비슷한 방식으로 액세스 할 수 있습니다.

for (auto& component: engineComponents) 
{ 
    component->Update(); 
} 

하지만 이번에는 호출 할 수있는 완전한 다형성 개체가 있습니다. 객체가 하위 클래스의 Update() 메소드를 오버로드하면이 메소드가 호출됩니다. 즉 당신이 필요하다면 당신은 또한 하위 클래스에 대한 포인터를 얻기 위해 캐스팅 사용할 수 있습니다

for (auto& component: engineComponents) 
{ 
    auto pDrawComponent = dynamic_cast<DrawableEngineComponent*>(component.get()); 
    if (pDrawComponent) 
    { 
     // it's drawable 
    } 
} 
+0

+1 (나는이 문제를 마침내 취소 할 수 있도록 핀과 바늘을 기다리고 있었기 때문에 투표를 할 수있었습니다). – WhozCraig

+0

이것은 정확히 내가 한 것입니다! 고마워요! 그러나, 나는'std :: shared_ptr'을 구현하지 않았고 모든 것이 잘 작동하는 것처럼 보입니다. 나는 그것의 장점과 단점이 무엇인지 모르겠다. 나는 그것을 읽어야 할 것이다. :) –

2

std::list<EngineComponent> 그냥이며, 엔진 컴퍼넌트 오브젝트의리스트 목록을 누르면 푸시 할 객체의 복사본을 만듭니다. 하위 클래스와 기본 클래스 간의 변환을 정의하지 않으면 실패합니다. 마찬가지로베이스에서 서브 클래스로 변환하려고 시도 할 때도 실패합니다.

기본 클래스 개체에 대한 포인터 목록이 필요합니다 (예 : std::list<unique_ptr<EngineComponent>>).당신이 사용하는 포인터의 어떤 유형, Draw 메서드 호출하기 전에 당신은 DrawableEngineComponent로 다운 캐스트해야합니다

 
for (unique_ptr<EngineComponent> & engCompPtr: engineComponents) 
{ 
    DrawableEngineComponent & drawableEngComp = dynamic_cast<DrawableEngineComponent &>(*engCompPtr); 
    drawableEngComp.Draw(); 
} 

나는 C 번호에 대해 많은 모르겠어요,하지만 당신은 개체를 작업 할 때 나는 가정 , 실제로는 일부 설명의 스마트 포인터에 의해 구현됩니다.

1

std::list<EngineComponent>EngineComponents의 묶음을 저장합니다. 당신이 목록에 DrawableEngineComponent를 추가하는 경우, 당신은 그것의 떨어져 "그릴 수"부분을 깔끔히됩니다

std::list<EngineComponent> engineComponents; 
EngineComponent comp1; 
engineComponents.push_back(comp1); // no problem, just copies it into the list 
DrawableEngineComponent comp2; 
EngineComponent newComp2 = *(EngineComponent*)(&comp2); // the drawable part is now sliced out! 
engineComponents.push_back(newComp2); // this adds the sliced version to the list 

, 당신은 EngineComponents에 대한 포인터의 목록을 저장하고자하는 것 이상을; 그러나 그것으로조차도 드로어 블을 따로 저장하는 것이 좋습니다. 그렇지 않으면 캐스팅과 체크를해야만 해당 타입으로 캐스팅 될 수 있습니다.