Я создаю класс, основанный на обратном вызове. Мне нужно дать клиенту свободу вызывать обратный вызов, который он определяет. Например, у меня есть класс Base:
class Base
{
...
public:
virtual void OnMsgReceived(const char *msg, char *&response);
virtual void OnMsgReceived(const char *msg, string &response);
};
Клиент ДОЛЖЕН реализовать либо один, но не 2. Как я знаю, какой он реализовал
чтобы при обработке обратного вызова я вызывал правильный?
Я знаю, что не могу сделать это во время создания объекта при создании объекта, но как только объект создается, можно ли мне проверить, какая из этих виртуальных функций была реализована? Благодарю.
В С ++ без чистого virtual
техника, нет способа узнать во время выполнения, если virtual
Функция реализована или нет классом.
Определите обе функции как чисто виртуальный функции и пусть клиент реализует их обоих. Вы все еще можете иметь тело чистой виртуальной функции для Base
если нужно.
Клиент ДОЛЖЕН реализовать либо один, но не 2.
Предполагая, что обе версии действительно необходимы, я не чувствую необходимости в вышеуказанном требовании.
Как я узнаю, какой из них он реализовал, чтобы при работе с
обратный звонок, я звоню правильный?
Как только вы назначите правильный объект Base
ссылка / указатель, вам не нужно беспокоиться об этом. Компилятор делает эту грязную работу за вас.
На боковой ноте, вы также можете исследовать CRTP техника. Где производный класс должен реализовать данный метод для передачи компиляции. Однако это будет не так гибко, как virtual
функциональность.
Я хотел бы добавить что-то к ответ Яммилинд:
Если вы используете чистую виртуальную функцию в классе, вы делаете этот класс абстрактным, и, как следствие, вы не можете создать его экземпляр.
если A абстрактный класс
class A
{
public:
//
virtual void foo() = 0;
//
}
ты не можешь писать
A myA
Один хороший ресурс об этом, как обычно, Веб-сайт Parashift C ++ FAQ.
Абсурдный подход: отправка в базовом классе от одного к другому. Если пользователь не переопределил хотя бы один из них, это приведет к переполнению стека обнаруживаемый:
void Base::OnMsgCallback(const char *msg, char*& response) {
std::string resp;
OnMsgCallback(msg,resp);
response = new char[resp.size()+1]();
std::copy(resp.begin(), resp.end(), response);
}
void Base::OnMsgCallback(const char *msg, std::string& response) {
char *resp;
OnMsgCallback(msg,resp);
response = resp;
delete [] resp;
}
Но это ужасный взлом, и, как вы могли заметить, char*
Версия требует ручной обработки ресурсов, что подвержено ошибкам. Вы гораздо лучше, определяя единственный интерфейс, который передает ссылку на std::string
(или имеет тип возврата std::string
, что упрощает договор (является response
INOUT или просто из параметр?) и код (не нужно создавать std::string
перед тем как позвонить
Нелегко определить, какая функция была переопределена — даже если вы можете, это также дизайн, которому разработчикам очень трудно следовать: Клиент ДОЛЖЕН реализовать либо один, но не 2 — разработчики могут забыть об этом, и им это может не понравиться!
Я бы предложил альтернативный способ, основанный на вашем дизайне:
Ваши рамки
class Base0
{
public:
// other basic members
};
template<class T>
class Base : public Base0
{
virtual void OnMsgReceived(const char *msg, T &response) = 0;
};typedef Base<char*> BaseChar;
typedef Base<string&> BaseString;void run(Base0* pBase)
{
const char* msg = getmsg();
BaseChar* p = dynamic_cast<BaseChar*>(pBase);
if(p != NULL)
{
char* response; //
p->OnMsgReceived(msg, response)
return;
}
BaseString* p2 = dynamic_cast<BaseString*>(pBase);
if(p2 != NULL)
{
string response; //
p2->OnMsgReceived(msg, response)
return;
}
}
Код клиента
class Derived: public BaseChar
{
public:
virtual void OnMsgReceived(const char *msg, char* &response)
{
// do sth
}
};
Пользователь вашего фреймворка должен реализовать либо BaseChar, либо BaseString, тогда все просто работает.
Этот дизайн плох по многим причинам, в любом случае есть способ сделать свой путь:
class Base
{
public:
virtual void OnMsgReceived(const char *msg, char *&response)
{
response = (char*)"OMG! nothing derived"; // or throw if you want
}
virtual void OnMsgReceived(const char *msg, string &response)
{
char *ptr_to_dest = 0;
OnMsgReceived(msg, ptr_to_dest);
response = ptr_to_dest;
}
};
Вы всегда позвоните тому, кто использует строка, вам не нужно проверять, что они сделали.
Вот что произойдет в зависимости от того, что они реализуют: