2011-02-04 5 views
2

У меня есть связанный список Foo объектов. Foo - это базовый класс, который наследует от него несколько классов. Скажем, классы A, B, and C.Вычисление класса наследуемого объекта

Я еду через этот связанный список и вызывая метод some_method, который имеет 3 определения; один для каждого дочернего класса:

some_method(A a); 
some_method(B b); 
some_method(C c); 

Связанный список является общим, поэтому он имеет тип Foo, как это имеет ассортимент A, B и C объектов.

Когда я еду через связанный список по адресу current_element, позвонив по телефону some_method(current_element);, как я могу заставить его вызвать правильный метод? Компилятор жаловался, пока не написал некоторый метод, который принял общий Foo, и он вызывает только этот метод.

ответ

3

В зависимости от ваших требований, вы можете захотеть использовать полиморфизм. Чтобы сделать это, добавьте чистый виртуальный метод в свой базовый класс узла и переместите соответствующие методы в производные классы.

class Foo 
{ 
public: 
    virtual void some_method() = 0; 
}; 

class A : Foo 
{ 
public 
    virtual void some_method() 
    { 
    // move the body of some_method(A a) here 
    } 
}; 

Для этой работы, ваш связанный список потребуется Foo*, вместо Foo.

class Node 
{ 
public: 
    Foo* foo; 
    Node* next; 
}; 

// ... 

Node* someNode = GetNode(); 
// Calls correct method - A::some_method, B::some_method, or C::some_method 
someNode->foo->some_method(); 

Если вы не можете положить some_method в Foo/A/B/C, то вы можете посмотреть в шаблон дизайна для посетителей:

http://en.wikipedia.org/wiki/Visitor_pattern

+0

Это почти звучит правильно, но Foo не имеют доступ к some_method, он передается в качестве параметра для метода. Он находится в классе, который имеет связанный список указателей Foo в качестве члена данных – bandai

0

RTTI Ваш друг здесь. Ниже приведен пример, приведенный в ссылке

+0

Я бы использовал только RTTI в качестве последней инстанции. Это последнее средство было бы, если бы я абсолютно не мог изменить «Foo». Если бы был пример, а не просто ссылка. –

-1

Вы можете вызвать метод для дочернего класса как метод-член. Например, A a = new A(); a.some_method() должен вызвать правильный метод. Внутри some_method() вы можете ссылаться на объект, используя ключевое слово this.

+0

Возможно, я не понимаю, но это не похоже, что это сработает. Полагаю, я должен уточнить; A - член данных, в котором хранится связанный список. Объект A не имеет доступа к some_method. some_method вызывается с объектом A в качестве параметра – bandai

+0

Ах, извините, я не понял, когда вы сказали «один для каждого дочернего класса», я думаю, я думал, что вы имели в виду, что каждый класс содержит определение some_method() –

1

двумя способами:

1) лучший способ:

Обратный дизайн, чтобы someMethod был виртуальным методом базового класса Foo и переопределял его в производных классах. Как:

class Foo { 
    public: 
     virtual void someMethod() = 0; 
}; 

class A { 
    public: 
     void someMethod() { /* implementation specific to A here */ }; 
}; 

class B { 
    public: 
     void someMethod() { /* implementation specific to B here */ }; 
}; 

class C { 
    public: 
     void someMethod() { /* implementation specific to C here */ }; 
}; 

Затем вызов SomeMethod на указатель Foo будет автоматически вызывать метод из соответствующего класса. Если это невозможно сделать, потому что someMethod не может быть реализован как часть Foo или его производных (например, ему нужен доступ к закрытым членам класса, в котором он находится в вашем дизайне), тогда вы можете попытаться разделить эту функциональность на подзадачи, которые могут быть введены в виртуальные методы этих классов ABC.

2) "У меня нет выбора" путь:

Использование RTTI (Run-Time Type Identification), он включен в C++. Это требует, чтобы ваш базовый класс Foo имел хотя бы один виртуальный метод.Вам нужно #include <typeinfo>, затем используйте указатель typeid(), он вернет объект type_info, и вы можете сравнить его результат name() с именами классов AB и C. Это не очень хороший подход, потому что он имеет больше накладных расходов и нарушает принципы проектирования ООП. Но если это единственный вариант, все в порядке.

2

Это проблема с «двойной отправкой». Вы можете использовать шаблон посетителя. Обычно посетитель является базовым классом, поэтому вы можете повторно использовать этот проект для множества проблем.

#include <iostream> 
class FooVisitor; 

class Foo 
{ 
public: 
    virtual void some_method() = 0; 
    virtual void visit(FooVisitor* v) = 0; 
}; 

class A; 
class B; 
class FooVisitor 
{ 
public: 
    virtual void visit(A* a){ std::cout << "A" << std::endl;} 
    virtual void visit(B* b){std::cout << "B" << std::endl;} 
}; 

class A : public Foo 
{ 
public: 
     virtual void some_method() 
    { 
     // move the body of some_method(A a) here 
    } 
    virtual void visit(FooVisitor* v) { v->visit(this);} 
}; 

class B : public Foo 
{ 
public: 
     virtual void some_method() 
    { 
     // move the body of some_method(A a) here 
    } 
    virtual void visit(FooVisitor* v) { v->visit(this);} 
}; 

int main() 
{ 
    FooVisitor fv; 
    Foo* f1 = new A; 
    f1->visit(&fv); 
    Foo* f2 = new B; 
    f2->visit(&fv); 
    getchar(); 
} 
+0

+1. Посетитель - хороший образец. У него есть недостатки (у посетителя должен быть новый метод для каждого производного типа и, следовательно, он должен быть изменен каждый раз, когда вы создаете новый производный класс). Но в целом это может быть хорошим выбором в такой проблеме. Мне нравится ваш пример кода. Однако эта проблема представляет собой разовую отправку, поэтому шаблон посетителя не является строго необходимым (нам не нужны производные посетители). –

+0

Спасибо. Реальный код NB требует много «const». – Keith