2013-03-29 4 views
0

가끔씩 프로퍼티 & 기본 클래스 포인터의 벡터에 저장되어있는 서브 클래스의 메소드에 액세스해야하기 때문에 프로그램에 디자인 문제가 있습니다. 그것은 보인다 경우서브 클래스의 프로퍼티/함수에 액세스하기 C++

void Thing::doB1Stuff() { 
    for(std::vector<Base*>::iterator it = bases.begin(); it != bases.end(); ++it) { 
    if (it->getB1()) { 
     //do stuff with b1 
    } 
    } 
} 

은 위의 코드는 작동해야하지만 :이 아래처럼 살아라에서 X 또는 Y에 액세스해야하는 경우

class B1; 
class B2; 
class Base { 
    private: 
    int id, a, b; 

    public: 
    virtual int getA() { return a; } 
    virtual int getB() { return b; } 
    virtual B1 *getB1() { return NULL; } //seems like a bad idea 
    virtual B2 *getB2() { return NULL; } //to have these two functions 
    Base(int newId) { id = newId; } 
}; 

class B1 : public Base { 
    private: 
    int x; 

    public: 
    int getX() { return x; } 
    B1 *getB1() { return this; } 
}; 

class B2 : public Base { 
    private: 
    int y; 

    public: 
    int getY() { return y; } 
    B2 *getB2() { return this; } 
}; 

class Thing { 
    private: 
    std::vector<Base*> bases; 

    void addBase(Base *base) { bases.push_back(base); } 
    void doB1Stuff(); 
    void doB2Stuff(); 
    void setAandB(int ID, int newA, int newB); //set a and b of one of the elements in bases vector based upon the id given 
}; 

문제는 : 내 코드는 다음과 같이 보입니다 하나는 쉽게 다음과 같이 B1/B2 속성을 사용하기 전에 포인터가 null의 경우 확인하는 것을 잊지 수 있기 때문에 나쁜 생각 :

따라서
void Thing::doB2Stuff() { 
    for(std::vector<Base*>::iterator it = bases.begin(); it != bases.end(); ++it) { 
    std::cout << it->getY(); //I believe this will crash the program if a NULL pointer is returned 
    } 
} 

내 질문은 : 서브 클래스의 속성에 액세스하는 좋은 방법은 무엇입니까? 내가 B1과 B2를위한 두 개의 별개의 벡터를 가지고 있다고 생각하고 있었지만 a와 b를 쉽게 설정할 수 있어야하기 때문에 좋지 않은 것 같습니다. 이견있는 사람?

+0

클래스가 해당 상황에서 근본적으로 다르게 동작하는 경우 배열에서 클래스를 혼합해서는 안됩니다. 대신에 템플릿을 사용하고 싶습니까? – Dave

+1

정확한 문제는'dynamic_cast'로 해결됩니다. (또는 더 나은 디자인) –

답변

0

액세스 할 항목이 올바른 하위 클래스 유형인지 여부를 확인할 수 있지만 이렇게하려면 런타임 유형 정보 (rtti)가 필요합니다.

그런 다음 특정 유형이고 null이 아닌 경우 해당 유형으로 캐스팅하고 올바른 기능을 호출 할 수 있습니다.

dynamic 또한이 기능을 사용하려면 rtti가 다시 필요하지만 본질적으로 자신을 확인한 다음 고정 캐스팅과 동일합니다.

1

당신이 가진 것은 완벽합니다. NULL을 포인터의 벡터 벡터 bases에 저장하지 않는 한, 반복기에서 반환 된 값을 null로 검사 할 필요가 없습니다. 불행하게도, 포인터의 벡터는 다형성 객체의 컨테이너에 대한 유일한 옵션입니다. 삭제 처리를 단순화하기 위해 공유 포인터 벡터를 만들 수 있지만 기본 아이디어는 동일하게 유지됩니다.

+0

마지막 문장에서'subst ("can", "should")'= P – WhozCraig

0

맞아요. 문제를 해결하는 좋은 방법이 아니므로 dynamic_cast을 사용하여 안전하게 사용할 방법을 결정할 수 있습니다.하지만 나쁜 코드 냄새가납니다.

하위 속성에 액세스하는 대신 기본 클래스에서 원하는 값을 반환하는 가상 함수를 만듭니다.

예 :

class Base { 
    private: 
    int id, a, b; 

    public: 
    virtual int getA() { return a; } 
    virtual int getB() { return b; } 
    virtual int getSubValue() = 0; // 
    Base(int newId) { id = newId; } 
}; 

class B1 : public Base { 
    private: 
    int x; 

    public: 
    int getSubValue() { return x; } 
}; 

class B2 : public Base { 
    private: 
    int y; 

    public: 
    int getSubValue() { return y; } 
}; 

그런 다음 당신은 그냥 - 그것을 호출 할 수 있습니다> getSubValue() 당신이 요청하는 하위 값을 얻을 수 있습니다.

이것은 내 의견이며이를 처리 할 수있는 방법이 많이 있지만, 이것은 내가 제공 한 정보를 기반으로 제안 할 것입니다.

관련 문제