У меня есть наниматель класса Message и класса Processor. Каждый процессор может получать одно или несколько сообщений на лету. Поскольку каждое сообщение может иметь некоторые отличающиеся атрибуты, я должен передать это сообщение конкретному классу сообщений, чтобы фактически обработать его.
Как нет. классов сообщений и классов процессов, я не хочу использовать dynamic_cast.
Я пытался использовать следующий код, но это дает ошибку времени компиляции.
Кроме того, у меня есть возможность прикрепить указатель процессора с сообщением (если необходимо), но не наоборот.
class Message
{
public:
virtual const Message* const getMessage() const = 0;
};
class MA : public Message
{
public:
const MA* const getMessage() const {return this;}
void printMA() const{std::cout<<"I am MA"<<std::endl;}
};
class MB : public Message
{
public:
const MB* const getMessage() const {return this;}
void printMB() const{std::cout<<"I am MB"<<std::endl;}
};
class Processor
{
public:
virtual void process(const Message* m) = 0;
};
class PA : public Processor
{
public:
void process(const Message* m) {processM(m->getMessage());}
void processM(const MA* m) {m->printMA();}
void processM(const MB* m) {m->printMB();}
};
int main()
{
Message* m1 = new MA();
Message* m2 = new MB();
Processor* p1 = new PA();
p1->process(m1);
p1->process(m2);
return 0;
}
Наконец, я использовал «двойную отправку», чтобы обойти это. Теперь, единственное, что мне нужно добавить функцию в класс MessageProcessor ‘всякий раз, когда я добавляю новый тип сообщения., Но я думаю, что это нормально.
class MessageProcessor
{
public:
virtual void process(const MA*) const{std::cout<<"unhandled:MA"<<std::endl;}
virtual void process(const MB*) const{std::cout<<"unhandled:MB"<<std::endl;}
virtual void process(const MC*) const{std::cout<<"unhandled:MC"<<std::endl;}
};
class Message
{
public:
virtual void process(const MessageProcessor*) const = 0;
};
class MA : public Message
{
public:
void printMA() const{std::cout<<"I am MA"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class MB : public Message
{
public:
void printMB() const{std::cout<<"I am MB"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class MC : public Message
{
public:
void printMC() const{std::cout<<"I am MC"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class Processor : public MessageProcessor
{
public:
void processM(const Message* m){m->process(this);}
};
class PA : public Processor
{
public:
void process(const MA* m) const {m->printMA();}
void process(const MB* m) const {m->printMB();}
};
class PB : public Processor
{
public:
void process(const MA* m) const {m->printMA();}
void process(const MC* m) const {m->printMC();}
};
int main()
{
const Message* m1 = new MA();
const Message* m2 = new MB();
const Message* m3 = new MC();
Processor* p1 = new PA();
p1->processM(m1);
p1->processM(m2);
p1->processM(m3);
Processor* p2 = new PB();
p2->processM(m1);
p2->processM(m2);
p2->processM(m3);
return 0;
}
Наиболее общее решение вашей проблемы, вероятно, Шаблон посетителя.
Самое простое, что нужно сделать, это устранить getMessage()
метод и сделать print()
чисто виртуальный в Message
и переопределить это в MA
а также MB
, Кроме того, вы можете сделать process()
чисто виртуальный метод в Process
и переопределить это в PA
, Смотрите код ниже:
#include <iostream>
class Message
{
public:
const std::string _id;
Message(std::string id):_id(id) {}
virtual void print() const = 0;
virtual void other_fun() const = 0;
};
class MA : public Message
{
private: double d_;
public:
MA():Message("MA"), d_(0.0) {}
virtual void print() const
{
std::cout<<"I am MA"<<std::endl;
std::cout << "I also have a double" << std::endl;
}
virtual void other_fun() const { std::cout << "I am MA specific" << std::endl; }
void do_hoops () const { std::cout << "Hoop!"<<std::endl;}
};
class MB : public Message
{
private: int i_;
public:
MB():Message("MB"), i_(0) {}
virtual void print() const
{
std::cout<<"I am MB"<<std::endl;
std::cout << "I also have an int"<<std::endl;
}
virtual void other_fun() const { std::cout << "I am MB specific" << std::endl; }
void do_twist() const { std::cout << "Twist!"<<std::endl; }
};
class Processor
{
public:
const std::string _id;
Processor(std::string id) : _id(id){}
virtual void process(const Message* m) = 0;
};
class PA : public Processor
{
public:
PA():Processor("PA") {}
virtual void process(const Message* m)
{
m->print();
m->other_fun();
}
};
int main()
{
Message* m1 = new MA();
Message* m2 = new MB();
// generic handling of message
Processor* p1 = new PA();
p1->process(m1);
p1->process(m2);
// message specific stuff
dynamic_cast<MA*>(m1)->do_hoops();
dynamic_cast<MB*>(m2)->do_twist();
return 0;
}
Выход на Ideone.
Приведения не требуются, виртуальные функции будут выбраны во время выполнения посредством динамической отправки (поиск виртуальной таблицы и т. Д.). Message
а также Process
являются абстрактными базовыми классами («интерфейсами») и MA
, MB
а также PA
конкретные классы, реализующие эти интерфейсы. В идеале вы также должны учитывать std::string
состояние из Message
интерфейс, но это осталось в качестве упражнения.
Приведение будет необходимо, если вы будете вызывать функции, специфичные для производного класса, и если вы знаете во время выполнения, что вы на самом деле вызываете такой класс. Это делается через dynamic_cast
на конкретный производный класс, на который в данный момент указывает указатель базового класса.
У вас есть недостаток дизайна. Подпись Processor::process
предполагает, что это занимает Message
то это не должно нарушать это обещание, пытаясь получить доступ к тому, что не является публичным интерфейсом Message
,
Ты можешь сделать Process
класс шаблона (хост), который наследуется от пользовательских политик. Политика здесь конкретная Message
классы. Что-то вроде этого:
#include <iostream>
struct MA
{
void print ()
{
std::cout << "MA: I'm the interface" << std::endl;
}
void printMA ()
{
std::cout << "MA: I'm special" << std::endl;
}
};
struct MB
{
void print ()
{
std::cout << "MB: I'm the interface" << std::endl;
}
void printMB ()
{
std::cout << "MB: I'm special" << std::endl;
}
};
template <typename M>
struct Process :
public M
{
void process()
{
M::print();
}
};
int main ()
{
Process<MA> p1;
Process<MB> p2;
p1.print(); // MA: I'm the interface
p1.printMA(); // MA: I'm special
p2.print(); // MB: I'm the interface
p2.printMB(); // MB: I'm special
}
Политики имеют print
метод, который определяет его интерфейс. У них также есть некоторые специальные методы, такие как printMA
а также printMB
, Хост класс (здесь Process
) действует как интерфейс пользователя для политик. Он может использовать методы интерфейса из классов политики. Пользователь может вызывать специальные методы политики через класс хоста.
Вы столкнулись с ограничением C ++. Что вам действительно нужно, так это чтобы полиморфизм работал с аргументами метода, а не только с методом, для которого вызываются аргументы. Обычно это называется двойная отправка. К сожалению, хотя есть какие-то обходные пути, я не видел идеальных. В этой статье Википедии показан общепринятый способ обхода (с использованием шаблона «Посетитель»).