Допустим, у меня есть 4 класса, B,C,D,E
которые наследуют от A
(абстрактный базовый класс).
Также у меня есть контейнер (std::vector
) типа A*
чье содержимое указывает на любой из
B,C,D,E
объекты.
Вот несколько правил:
Если B
объект и C
объект взаимодействует, они удаляются из вектора и на их месте D
объект создается.
Также, C + D = E
Теперь предположим, что я случайно выбрал одно из указанных векторов содержимого; Как мне узнать, какой объект какого типа, чтобы реализовать механизм взаимодействия?
ПРИМЕЧАНИЕ: я не хочу использовать typeid
оператор, динамическое приведение или флаги. Любые другие решения?
Вот код
#include <iostream>
class A {
protected:
int v;
public:
A(){}
~A(){}
};
class B :public A {
public:
B(){}
~B(){}
};
class C : public A {
public:
C(){}
~C(){}
};
class D : public A {
public:
D(){}
~D(){}
};
class E : public A {
public:
E(){}
~E(){}
};
int main()
{
std::vector<A*> container;
return 0;
}
Как бы я реализовал функцию (и) взаимодействия?
Вы можете использовать виртуальные функции для многократной отправки
struct B;
struct C;
struct D;
struct E;
struct A
{
virtual ~A() = default;
virtual std::unique_ptr<A> interactWithA(const A&) const = 0;
//protected:
virtual std::unique_ptr<A> interactWithB(const B&) const = 0;
virtual std::unique_ptr<A> interactWithC(const C&) const = 0;
virtual std::unique_ptr<A> interactWithD(const D&) const = 0;
virtual std::unique_ptr<A> interactWithE(const E&) const = 0;
};
// Your interact rules
template <typename LHS, typename RHS>
std::unique_ptr<A> interact(const LHS&, const RHS&) { return nullptr; }
// Note that definitions and declarations must be split in reality
// to be able to compile it
std::unique_ptr<A> interact(const B&, const C&) { return std::make_unique<D>(); }
std::unique_ptr<A> interact(const C&, const D&) { return std::make_unique<E>(); }
// Maybe the reflexive case, C/B D/C ?// The derived classes
struct B : A
{
std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithB(*this); }
// Even if code look similar for other inherited class
// the difference is in the runtime type of the objects are known.
std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
};
struct C : A
{
std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithC(*this); }
std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
};
struct D : A
{
std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithD(*this); }
std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
};
struct E : A
{
std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithE(*this); }
std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
};
а потом
std::vector<std::unique_ptr<A>> v /* = .. */;
auto a = v[i]->interactWithA(*v[j]);
if (a) {
// Remove v[i] and v[j]
// Insert a
}
Ваша проблема звучит как плохая абстракция. Так что на самом деле вы не решаете правильную проблему. Вы должны использовать наследование, когда вам не нужно знать точный тип объекта, но вместо этого полагаться на полиморфизм во время выполнения.
Вы можете использовать некоторые флаги, например, виртуальную функцию, которая будет возвращать идентификатор каждого типа, но это скорее обходной путь, а не решение. Также легко ошибиться.
class A
{
...
virtual int get_id() = 0;
}
Варианты
Вместо полиморфизма, если типы являются фиксированными (например, вы не планируете добавлять или удалять классы), вы можете использовать std::variant<>
(C ++ 17) или boost.variant. Для взаимодействия с ним вам нужно будет использовать посетителей и звонить std::visit()
. Может быть, с ним будет сложнее взаимодействовать, но, на мой взгляд, он лучше подойдет в качестве решения описанной вами проблемы.