Заставить производный класс переопределить хотя бы одну виртуальную функцию
Представьте себе этот простой базовый класс:
struct simple_http_service
{
virtual reply http_get(…);
virtual reply http_post(…);
virtual reply http_delete(…);
// etc.
};
Я хотел бы запретить пользователю выходить из этого класса, не переопределяя хотя бы один из них, и не дать ему реализовать его simple_http_service
Есть ли хороший способ сделать это?
4 ответа
Это звучит как очень странное ограничение. Во что бы то ни стало защитите пользователя от неправильного использования, но не пытайтесь запрещать вещи, которые вы просто "не видите". Если нет смысла выводить из вашего класса, не переопределяя ни одну из трех функций, тогда позвольте пользователю переопределить столько функций, сколько ему захочется, и поверьте, что он не сделает бессмысленную задачу выведения без переопределения какой-либо из функций. функции. В этом нет никакого вреда для пользователя, просто это не очень полезно.
Но если вам нужно обеспечить это (опять же, я бы посоветовал вам переосмыслить), то не используйте виртуальные функции. Вместо этого передайте указатели на функции или функциональные объекты (или std::function
/ boost::function
) обратные вызовы. Сделайте так, чтобы базовый класс выглядел примерно так:
struct simple_http_service
{
typedef std::function<reply (...)> func_type;
reply http_get(...) { return get_func(...); }
reply http_post(...) { return post_func(...); }
reply http_delete(...) { return delete_func(...); }
// etc.
private:
func_type get_func;
func_type post_func;
func_type delete_func;
};
Теперь просто добавьте необходимые конструкторы (или свободные / статические функции, чтобы вы могли назвать их, чтобы избежать неоднозначности), чтобы экземпляр класса мог быть создан только тогда, когда предоставлен хотя бы один из объектов функции.
Я думаю, что все эти функции должны быть чисто виртуальными. Структура, которую вы разместили, по сути является интерфейсом. Если функции не все необходимы, производные структуры должны просто предоставлять пустую реализацию для функций, которые не имеют к ним отношения.
Если вы просто хотите, чтобы базовый класс был абстрактным, предоставьте ему чисто виртуальный деструктор и сделайте ваши другие функции обычными виртуальными.
Я не совсем понимаю, почему вы хотите предоставить реализацию по умолчанию для двух других функций, но требует, чтобы по крайней мере одна из них определялась пользователем в случае http-запросов. Понятно, что все функции используют друг друга для реализации некоторых функций с использованием существующего кода. Представьте себе пример с этим классом:
class Cls
{
public:
virtual std::string toString()=0;
virtual std::string serialize()=0;
};
Существует класс, который можно преобразовать в строку и сериализовать в строку. Но если один из них не реализован, вы вместо этого хотите вызвать второй, так что это будет вариант:
class Cls
{
public:
virtual std::string toString() //calls serialize() by default
{
return this->serialize();
}
virtual std::string serialize() //calls toString()
{
return this->toString();
}
virtual ~Cls()=0; //force the class to be abstract
};
Но теперь существует проблема с производным от Cls, но не переопределением хотя бы одной из функций. Если переопределение не выполняется, во время выполнения вы просто вводите бесконечную рекурсию. Если это одна из ваших проблем, есть решение во время выполнения, приведенный ниже код просто ничего не делает, если возникает такая проблема.
class Cls
{
public:
virtual std::string toString()
{
if ((void*)(this->*(&Cls::serialize)) != (void*)(&Cls::serialize))
{//checks if the current implemetation is not equal to the default one
return this->serialize();
}
else
{
return ""; //default return value
}
}
virtual std::string serialize()
{
if ((void*)(this->*(&Cls::toString))!=(void*)((&Cls::toString)))
{
return this->toString();
}
else
{
return "";
}
}
};
Это компилируется в GCC, но заполняет ваш экран предупреждениями о странном преобразовании из funcptr в void*. По крайней мере, это работает как задумано. Могут быть некоторые метапрограммирующие решения времени компиляции, нужно подумать об этом.
Если вы знаете, какие методы вы хотите переопределить производным классом, просто объявите этот метод чисто виртуальным.
Например, чтобы сделать http_get чисто виртуальным:
struct simple_http_service
{
virtual reply http_get(…) = 0;
virtual reply http_post(…);
virtual reply http_delete(…);
// etc.
};