как вызвать функцию-член производного класса без даункинга

У меня есть наниматель класса 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;
}

2

Решение

Наконец, я использовал «двойную отправку», чтобы обойти это. Теперь, единственное, что мне нужно добавить функцию в класс 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;
}
2

Другие решения

Наиболее общее решение вашей проблемы, вероятно, Шаблон посетителя.

1

Самое простое, что нужно сделать, это устранить 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 на конкретный производный класс, на который в данный момент указывает указатель базового класса.

1

У вас есть недостаток дизайна. Подпись 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) действует как интерфейс пользователя для политик. Он может использовать методы интерфейса из классов политики. Пользователь может вызывать специальные методы политики через класс хоста.

1

Вы столкнулись с ограничением C ++. Что вам действительно нужно, так это чтобы полиморфизм работал с аргументами метода, а не только с методом, для которого вызываются аргументы. Обычно это называется двойная отправка. К сожалению, хотя есть какие-то обходные пути, я не видел идеальных. В этой статье Википедии показан общепринятый способ обхода (с использованием шаблона «Посетитель»).

1
По вопросам рекламы [email protected]