Это может быть глупый вопрос, я подозреваю, что знаю ответ (нет), потому что я, кажется, здесь бью стену.
Учитывая, что у меня есть коллекция объектов, полученных из определенного класса:
class BaseClass;
class DerivedA: public BaseClass;
class DerivedB: public BaseClass;
class DerivedC: public BaseClass;
std::vector<BaseClass> myCollection;
Я хочу вызвать метод в зависимости от типов конкретного класса:
class Processor {
void doSomething(DerivedA a, DerivedB b);
void doSomething(DerivedA a, DerivedC c);
}
Проблема в том, что если я получу доступ к отдельным элементам в коллекции и попытаюсь вызвать метод doSomething в «Процессоре», он не сможет решить, какой метод использовать (afaik). Итак, мой вопрос: есть ли способ получить элементы в коллекции с правильным производным типом?
Если вы собираетесь сохранить doSomething
метод, как он есть, это то, что называется многократная отправка и в настоящее время НЕ поддерживается C ++.
Если бы это была функция виртуального члена BaseClass
тогда да, это будет запуск полиморфизма мельницы C ++ на объекте, к которому он вызывается, но он все равно НЕ будет автоматически определять тип аргумента.
Чтобы обойти это, вы можете сделать что-то вроде того, что предлагается в предыдущей ссылке
void collideWith(Thing& other) {
// dynamic_cast to a pointer type returns NULL if the cast fails
// (dynamic_cast to a reference type would throw an exception on failure)
if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
// handle Asteroid-Asteroid collision
} else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
// handle Asteroid-Spaceship collision
} else {
// default collision handling here
}
}
По сути, продолжайте приведение к различным возможным классам Derived, пока один из них не сработает и не вызовет один из методов соответствующим образом (без особых усилий, так как компилятор знает, к какому типу вы пытаетесь привести).
ВАЖНО: как указывает @WhozCraig, ваш вектор должен содержать указатели, чтобы избежать Объектно-нарезка и сделать весь этот вопрос спорным.
Хорошо, да, вы должны использовать полиморфизм, как указано выше. Если ваша функция должна обрабатывать 2 объекта, хотя это становится чрезвычайно сложным.
Если деривации образуют ограниченный набор и знают друг друга, вы можете использовать двойную рассылку. Это не идеально, но это решает этот конкретный случай.
class DerivedA;
class DerivedB;
class DerivedC;
class BaseClass
{
public:
virtual ~BaseClass();
virtual void doSomethingWithBase( BaseClass & b2 ) = 0;
virtual void doSomethingWithDerivedA( DerivedA & da ) = 0;
virtual void doSomethingWithDerivedB( DerivedB & db ) = 0;
virtual void doSomethingWithDerivedC( DerivedC & dc ) = 0;
};
class DerivedA : public BaseClass
{
public:
void doSomethingWithBase( BaseClass & b2 )
{
b2.doSomethingWithDerivedA( *this );
}
void doSomethingWithDerivedA( DerivedA & da )
{
// implement for two DerivedA objects
}
void doSomethingWithDerivedB( DerivedB & db )
{
// implement for an A and B
}
void doSomethingWithDerivedC( DerivedC & dc )
{
// implement for an A and C
}
};
// implement DerivedB to call doSomethingWithDerivedB on its parameter
// implement DerivedC to call doSomethingWithDerivedC on its parameter.
Вы поняли идею. Оттуда, где вы звоните, вам не нужно знать, какие у вас есть два типа, и вам никогда не нужно искать это. Но если вы когда-нибудь добавите больше реализаций, у вас будет много кода для редактирования, и вы можете рассмотреть какую-то таблицу поиска.
Если вам нужен класс для определения самого себя, вы можете использовать какой-то виртуальный идентификатор.
class BaseClass
{
public:
virtual int id() const = 0;
};
а затем вы получаете классы, чтобы показать их идентификаторы и найти обработчик в таблице на основе этих идентификаторов, которые будут обрабатывать два объекта. Идентификаторы не обязательно должны быть целочисленными, они могут быть строками, что упрощает предотвращение конфликтов имен, и это имеет преимущество перед методом двойной диспетчеризации базового класса, который не знает его производные классы или не знает их друг друга, и быть расширяемым. Вы также не должны обращаться с каждой парой.