2013-04-12 2 views
1

모든 다른 객체가 Zoo인데이 객체에는 Collection = vector<Animal>이라는 멤버 변수가 있습니다. 모든 요소가 Birds이고 하나가 모든 요소가 Bats 인 동물 인 한 동물원을 가지고 있습니다. BirdsBats은 동물에서 파생됩니다.파생 클래스 C++의 메소드에 액세스하는 가장 좋은 방법

모든 조류와 박쥐를 호출 할 수있는 fly() 메서드가 있어야합니다. 그러나 이것을 수행하는 최선의 방법은 확실하지 않습니다. 새/박쥐 동물원을 돌 때 그냥 던져 야하나요? 이와 같이 :

Bird thisBird = static_cast<Bird>(Collection[i]); 
thisBird.fly(); 

... 또는 어떻게 든 그것으로부터 파생 된 클래스에서만 구현되는 Animal에 가상 함수를 가질 수 있습니까?

아이디어가 '좋은 코드'인 이유와 그에 대한 타당성을 환영합니다!

+2

컨테이너 내에서 값으로 * 다형 적으로 사용하기 쉬운 개체를 넣을 것입니다. 그 [잘 끝나지 않을 것입니다] (http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c). – Jon

+0

각 동물원의'Collection' 벡터는 그 안에 (generic, bird, bat 중 하나와 같은 종류의) 동물을 가지고 있습니다.캐스팅하지 않으면 어떻게해야하는지에 대한 제안 사항이 있습니까? – tiswas

+0

'동물'이란 무엇입니까? – juanchopanza

답변

1

는 오, 그래, Animal는 가상 함수를 가질 수있다. 움직일 새를 주문하면 fly()으로 전화 할 수 있습니다.

비행하는 동물에 대해 fly()을 정확하게 정의하면 Colection[i]->fly()이 올바른 작업을 수행합니다. 이렇게하면 코드가 단순 해집니다.

하지만 컬렉션을 사용하려면 동물 개체뿐만 아니라 동물에 대한 포인터를 수집해야합니다. 난 당신이

struct Animal {virtual bool fly(){return false;  }}; 
struct Bird:public Animal{bool fly(){cout<<"fly\n";return true;}}; 

같은 것을 구현하기 위해 그리고 당신은 별도의 canFly를 필요 없어요 추천 할 수있는 다른 답을 읽기

#include <vector> 
#include <memory> 
#include <iostream> 
using namespace std; 
struct Animal{virtual void fly(){}}; 
struct Bird:public Animal{void fly(){cout<<"fly\n";}}; 
int main() 
{ 
    vector<unique_ptr<Animal>> Colection(1); 
    Colection[0].reset(new Bird); 
    Colection.push_back(unique_ptr<Animal>(new Bird)); 
    Colection.push_back(unique_ptr<Animal>(new Animal)); 
    Colection[0]->fly(); 
    Colection[1]->fly(); 
    Colection[2]->fly(); 
} 

: 예를 들어 .

+0

이것은 C++ 11을 사용하지는 않지만 나에게 가장 단순한 내기처럼 보일 것이다. 그래서 나는 단지 벡터 을 가져야 만한다고 생각한다. 컬렉션; – tiswas

1

모든 동물이 비행 할 수있는 것은 아니므로 일반적인 행동을 위해 특정 행동 (예 : 모든 동물 간 흔하지 않음)을 수행 할 필요가 없습니다. 공통된 행동 (각 파생 클래스별로 다르게 구현)을 허용 할 목적으로 Animal 클래스 (또는 파생 클래스) 컬렉션이있는 경우

인터페이스 (즉, 추상적 인 클래스)는 FlyAble을 구현하고 실제로 날아갈 수있는 동물에 의해서만 확장 할 수 있습니다. 이러한 유형의 동물을 위해 지정된 다른 컬렉션 (예 : < FlyAble>)에 저장하면됩니다.

+0

대신에 'Vector '을 제외하면 각 동물원의 컬렉션이 다른 유형 (Vector 인데 Vector 이 아닌)이기 때문에 각 동물원이 다른 유형이어야 할 필요는 없습니까? – tiswas

+0

이것은 프로그램에서 동물원의 역할에 따라 크게 다릅니다. 그리고 만약 당신이 그것을 올바르게 설계한다면 당신은이 비행 환경에서 단지 하나의 동물원 유형을 가질 것이라고 믿습니다. – giorashc

+0

downvoter는 그림자에서 나와서 설명해 주시겠습니까? – giorashc

1

당신은 아마 Bird 같은 계층 구조를하지 않고 Bat 당신이에서 제공하는 하위 클래스에 선언 fly() 방법없이 Animal에서 발생한다. 그렇게하면 통일 된 계층 구조의 포인트를 잃어 버리게됩니다.

는 당신도 할 수있다 :

class Animal { 
    virtual bool canFly() { return false; } 
    virtual void fly() {throw Exception("Sorry babe, I don't fly"); } 
} 

그리고 BirdBatfly 우선합니다.

이렇게하면 개가 아닌 고양이에게 fly() 메서드가 구현됩니다.

class Flyer : public Animal { 
    virtual void fly() = 0; 
} 

class Bat : public Flyer {} 
class Bird : public Flyer {} 

Reptile, Mammal 같은 더 자세한 생물학적 분류와 일치하지 않을 것이다 : 그래서 어쩌면 당신은이 방법을 선언 할 새로운 클래스 Flyer를 만들 수 있습니다.

또 다른 속임수는 move()과 같은 메서드를 제안 할 수 있으며 개는 run() 및 bird를 fly()으로 구현하며 모두 통합 된 인터페이스를 사용합니다.

다른 점은 개가 날 수 있는지 여부를 묻는 질문이 유효하다고 생각하므로 Dog.canFly()과 같은 메소드를 동물의 일부로 구현해야한다고 생각합니다.

이 나는이에 가고 싶어, 고려 모든 :

// Your base animal 
class Animal { 
    virtual bool canFly() {return false;} 
}; 

// Any animal that could fly, just abstract class 
class Flyer { 
    virtual void fly() = 0; 
} 

// Ants, flies etc.; real biological hierarchy 
class Insect : public Animal {} 
class Mammals : public Animals {} 
class Birds : public Animals {} 

// And now flying bird : 
class Bird : public Birds, public Flyer { 
    virtual bool canFly() {return true; } 
    virtual void fly() {...} 
} 

// And flying insect 
class Fly : public Insect, public Flyer { 
    virtual bool canFly() {return true; } 
    virtual void fly() {...} 
} 

그리고 당신은 다음 (난 당신이 이것에 대한 포인터를 사용해야합니다 것을 두려워 this answer 기준) 할 수 있습니다 :

if(Collection[i]->canFly()){ 
    Flyer *thisBird = static_cast<Flyer*>(Collection[i]); 
} 

는 또한 Animal에서 Flyer을 유도하고 가상 상속을 사용하지만, 이것은 "공포의 다이아몬드"이라고하며 not considered good practice하지만, 읽는 가치가 있었다.


바와 같이 canFly()을 지정해야합니다 코멘트에서 Ricibob, Flyer 지적하는 것입니다 간단한 예 :

class Flyer { 
public: 
    virtual bool canFly() const {return true;} 
    virtual void fly() {cout << "Flying" << endl; } 
}; 

Bird b; 
cout << "Can fly: " << b.canFly() << endl; 
// Error 1 error C2385: ambiguous access of 'canFly' 

하지만 Animal에서 제공 Flyer을하고 수업 BirdsBird 일부 경우 Flyer :

class Animal { 
public: 
    virtual bool canFly() const {return false;} 
}; 

class Flyer : virtual Animal { 
public: 
    virtual bool canFly() const {return true;} 
    virtual void fly() {cout << "Flying" << endl; } 
}; 

class Bird : virtual public Birds, virtual public Flyer { 

}; 

// Warning 1 warning C4250: 'Bird' : inherits 'Flyer::Flyer::canFly' via dominance 

지금 canFly()1을 반환하지만 코드가 잘못되었습니다. 이 시점에서

... 당신은 수동으로 각각의 서브 클래스 (또는 대형 그룹)에 대한 canFly()을 지정하거나 ( Chicken 예를 바로하지 않은) Flyers에서 Birds을 제공하거나, 새로운 서브 클래스 제공 :

class FlyingBrids : public Birds, public Flyer /* Flyer not delivered from Animal */ { 
    virtual bool canFly() const {return true;} 
}; 

FlyInsect에서 전달되므로 Flyer이 여전히 중요하다는 점에 유의하십시오. 당신이 개를 주문하면

virtual void move(){} 
virtual void fly(){} 

그것은 아무것도하지 않고 비행 :

+0

음,이 소리가 마음에 들지만, @ 존이 언급 한 바 : '벡터 이 있고 내부에 새를 넣으면 해당 새가 슬라이스가되어 벡터에 더 이상 캐스트 할 수없는 동물이 들어갑니다 Bird ' – tiswas

+0

@tiswas 솔직히 말해서 나는 항상 포인터를 사용했는데 문제가되지 않았다. 그냥 재미로 복사 생성자를 구현하고 호출 된 횟수를 확인하십시오. :) – Vyktor

+0

@Vyktor 플라이어로 돌아가는 canFly 기본 임 플리 멘 테이션을 이동하는 것이 합리적입니다. (플라이어는 기본적으로 비행 할 수 있습니다!) – Ricibob