Классический полиморфный подход во время выполнения:
struct Operation
{
virtual ~Operation() { } // guideline: if there are any virtual functions,
// provide virtual destructor
virtual int doStuff(int, int) const;
};
struct Subber : Operation
{
int doStuff(int a, int b) const { return a - b; }
};
struct Adder : Operation
{
int doStuff(int a, int b) const { return a + b; }
};
enum Operations { Add, Subtract };
struct Operation* op_factory(Operations op)
{
if (op == Add) return new Adder;
if (op == Subtract) return new Subber;
throw std::runtime_error("unsupported op");
}
int main()
{
Operation* p1 = op_factory(Add);
std::cout << p1->doStuff(1,3) <<std::endl;
Operation* p2 = op_factory(Subtract);
std::cout << p2->doStuff(1,3) <<std::endl;
delete p1;
delete p2;
}
Из стандарта 5.3.5 / 5 "В первом варианте (удаление объекта), если статический тип операнда отличается от его динамического типа, статический тип должен быть базовым классом динамического операнда Тип и статический тип должны иметь виртуальный деструктор или поведение не определено. ", поэтому вы должны использовать ключевое слово virtual
в деструкторе базового класса.
Примечательно, что в вашем примере тип выполняемой операции был передан классу-оболочке с использованием аргумента функции 0 или 1 ... это то, что предполагает, что вам нужен полиморфизм во время выполнения. Например, если значение 0 или 1 основано на аргументе командной строки, содержимом файла, вводе с клавиатуры и т. Д., То описанный выше фабричный метод может передать соответствующее значение «Добавить» или «Вычесть» и получить объект с соответствующим поведением, полученный из операции. Эта концепция создания экземпляра полиморфного типа во время выполнения на основе значений времени выполнения называется фабрикой.
Если вам действительно нужен только полиморфизм во время компиляции, вы можете сделать некоторые интересные вещи с помощью таких шаблонов, как:
template <class Operation>
void output(int a, int b)
{
std::cout << Operation::doStuff(a, b) << std::endl;
std::cout << Operation::doStuff(a * 10, b * 10) << std::endl;
std::cout << Operation::doStuff(a * 100, b * 100) << std::endl;
}
int main()
{
output<adder>(1, 3);
output<subber>(1, 3);
}
FWIW, ваш подход, вероятно, немного быстрее, чем подход виртуальной функции (поскольку он потенциально может сделать больше встраивания), но не такой чистый, расширяемый, обслуживаемый или масштабируемый.