Я знаю, что есть еще один очень похожий пост здесь, на SO. Но я думаю, что это немного по-другому. Основной вопрос здесь заключается не в обсуждении преимуществ или недостатков некоторых методов (хотя это выглядит естественным образом), а , какие методы существуют для достижения полиморфного поведения и какой является лучшим в отношении некоторых критерии (см. ниже) .
Предположим, у меня есть класс со следующей структурой
class Evaluator
{
public :
double Evaluate( double x )
{
/*something here*/
}
};
где мы можем выбрать различную реализацию функции Evaluate
от экземпляра к экземпляру. Я хочу найти оптимальное (в некотором смысле) решение по следующим критериям.
- Реализация функции
Evaluate
(что такое Evaluate
) определяется на этапе построения экземпляра
- Должно быть как можно проще добавить новую реализацию
Evaluate
функции
Evaluate
предназначена для использования в качестве метода abstract class
- Предназначен для использования не только метод
Evaluate
, но и его производная. Так что было бы хорошо, если бы функция и ее производная жили вместе
Решения, которые я нашел, могут помочь понять, чего я хочу достичь.
Использование указателя на функцию
//somewhere in someheader.h
#include <cmath>
namespace evals
{
inline double Sin( double x ) { return sin(x); }
inline double Sigmoid( double x ) { return 1. / ( 1. + exp( -x ) ); }
}
//somewhere in Evaluator.h
class Evaluator
{
protected :
double (*_eval)( double );
public :
Evaluator( double (*eval)( double ) ) : _eval( eval ) { }
double Evaluate( double x ) { return _eval( x ); }
};
//somewhere in test.cpp
#include "someheader.h"
#include "Evaluator.h"
Evaluator e( &evals::Sin );
e.Evaluate( 3.14159 );//returns sin( 3.14159 )
Использование виртуальных функций
//somewhere in someheader2.h
#include <cmath>
namespace evals
{
class AbstractEvaluate
{
public :
virtual double Return( double x ) = 0;
};
//concrete evals
class Sin : public AbstractEvaluate
{
public :
double Return( double x ) { return sin( x ); }
};
class Sigmoid : public AbstractEvaluate
{
public :
double Return( double x ) { return 1. / ( 1. + exp( -x ) ); }
};
}
//somewhere in Evaluator2.h
#include <string>
#include "someheader2.h"
class Evaluator
{
protected :
AbstractEvaluate* _eval;//cannot have an instance, only a pointer
public :
Evaluator( std::string evalName )
{
//according to some rule return pointer to desired concrete evaluate class e.g.
if( evalName == "Sin" ) { _eval == new evals::Sin; }
else if( evalName == "Sigmoid" ) { _eval == new evals::Sigmoid; }
else { /*some default behavior*/ }
}
double Evaluate( double x ) { return _eval->Return( x ); }
}
//somewhere in test.cpp
#include "Evaluator2.h"
Evaluator e( "Sin" );
e.Evaluate( 3.14159 );//returns sin( 3.14159 )
Обсуждение
Хотя второй подход более привлекателен для моей личной проблемы (см. Последний критерий), я вижу опасного оператора new
. Действительно ли это проблема, и может ли ее решить соответствующий деструктор? В чем еще может быть проблема с решениями, которые я предоставил? И главный вопрос: как лучше всего делать то, что я хочу? .