C++ Определение перегруженного оператора виртуального базового класса

Я пытаюсь написать набор общих математических служебных классов (искатели корней, интеграторы и т. д.), которые при построении принимают указатель на базовый класс, определяющий функцию, с которой я хочу, чтобы работал конкретный алгоритм. Базовый класс должен определять только общедоступный виртуальный интерфейс (абстрактный или с тривиальной функциональностью по умолчанию) type operator()(type inputArg), который может быть реализован пользователем по мере необходимости. Это позволит пользователю просто реализовать необходимые функторы и выполнить необходимые вычисления. Мой mwe ниже:

Этот первый заголовок определяет абстрактный класс интерфейса.

// BaseFunctor.h

#ifndef _BASE_FUNCTOR_H_
#define _BASE_FUNCTOR_H_

class BaseFunctor
{
public:
   virtual double operator() (double x) = 0;
};
#endif

Это класс для одного из методов решателя

// NewtonsMethod.h

#ifndef _NEWTONS_METHOD_H_
#define _NEWTONS_METHOD_H_

class BaseFunctor;

class NewtonsMethod
{
public: 

   NewtonsMethod(BaseFunctor *rhsIn,
                 BaseFunctor *rhsPrimeIn,
                 double       x0In);

   ~NewtonsMethod();

   bool DetermineRoot(double &root);

 private:

   double       x0;
   BaseFunctor *rhs;
   BaseFunctor *rhsPrime;

   static const double       EPSILON;
   static const unsigned int MAX_ITER;

};
#endif

Это реализация решателя: // NewtonsMethod.cpp

#include "NewtonsMethod.h"
#include "BaseFunctor.h"
#include <cmath>

const double       NewtonsMethod::EPSILON  = 1e-9;
const unsigned int NewtonsMethod::MAX_ITER = 30;

NewtonsMethod::NewtonsMethod(BaseFunctor *rhsIn,
                             BaseFunctor *rhsPrimeIn,
                             double       x0In) :
   rhs     (rhsIn),
   rhsPrime(rhsPrimeIn),
   x0      (x0In)
{ }

NewtonsMethod::~NewtonsMethod() { }

bool NewtonsMethod::DetermineRoot(double &root)
{
   // This is obviously not implemented
   root = rhs(1.0) / rhsPrime(2.0);
   return false;
}

И основная функция, в которой я делаю реализации производного класса: // Main.cpp

#include "BaseFunctor.h"
#include "NewtonsMethod.h"
#include <iostream>
#include <iomanip>

class fOfX : public BaseFunctor
{
   double operator() (double x)
   {
      return x * x - 2.0;
   }
};

class fPrimeOfX : public BaseFunctor
{
   double operator() (double x)
   {
      return 2.0 * x;
   }
};


int main()
{
   double x0 = 2.0;

   BaseFunctor *f      = new fOfX();
   BaseFunctor *fPrime = new fPrimeOfX(); 

   NewtonsMethod newton(f, fPrime, x0);

   double root      = 0.0;
   bool   converged = newton.DetermineRoot(root);

   if (converged)
   {
      std::cout << "SUCCESS: root == " << std::setprecision(16) << root << std::endl;
   }
   else
   {
      std::cout << "FAILED: root == " << std::setprecision(16) << root << std::endl;
   }
   delete f;
   delete fPrime;
}

Я постарался сделать это как можно короче, так что извините, если получилось слишком длинно. В основном я получаю ошибку:

g++ Main.cpp NewtonsMethod.cpp -o main
NewtonsMethod.cpp: In member function ‘bool NewtonsMethod::DetermineRoot(double&)’: 
NewtonsMethod.cpp:29: error: ‘((NewtonsMethod*)this)->NewtonsMethod::rhs’ cannot be used    as a function
NewtonsMethod.cpp:29: error: ‘((NewtonsMethod*)this)->NewtonsMethod::rhsPrime’ cannot be   used as a function

Как я могу решить эту проблему, сохранив желаемую функциональность или создав класс для различных необходимых функций?

Спасибо


person JohnML    schedule 04.07.2013    source источник


Ответы (1)


rhs и rhsPrime — указатели. Вам нужно сослаться на них, чтобы вызвать оператор вызова функции.

(*rhs)(1.0) / (*rhsPrime)(2.0)

Если rhs и rhsPrime требуются (т.е. не могут быть NULL) и не могут быть изменены после того, как объект NewtonsMethod имеет конструктор, вы должны объявить их как ссылки, а не указатели. Это также избавит от необходимости разыменовывать их для вызова оператора вызова функции.

В приведенном ниже примере показано, как использовать ссылки для ссылки на функторы.

class NewtonsMethod
{
public: 
   NewtonsMethod(BaseFunctor& rhsIn,
                 BaseFunctor& rhsPrimeIn,
                 double       x0In);

   ~NewtonsMethod();

   bool DetermineRoot(double &root);

 private:

   double       x0;
   BaseFunctor& rhs;
   BaseFunctor& rhsPrime;

   static const double       EPSILON;
   static const unsigned int MAX_ITER;
};

int main()
{
   double x0 = 2.0;

   fOfX       f;
   fPrimeOfX  fPrime;

   NewtonsMethod newton(f, fPrime, x0);
}

...or...

int main()
{
   double x0 = 2.0;

   BaseFunctor *f      = new fOfX();
   BaseFunctor *fPrime = new fPrimeOfX(); 

   NewtonsMethod newton(*f, *fPrime, x0);

   // ... other code including delete for the functors
}
person Captain Obvlious    schedule 04.07.2013
comment
Спасибо! Разыменование их работает, хотя это уродливое решение. Как я могу использовать их в качестве ссылок, поскольку я ссылаюсь на них как на объект BaseClass? Я не могу статически разместить их в main, верно? - person JohnML; 04.07.2013
comment
Я обновил свой ответ, включив в него пример использования ссылок. Вам, конечно, по-прежнему необходимо убедиться, что время жизни объектов функтора равно или больше, чем у объекта NewtonsMethod. - person Captain Obvlious; 04.07.2013
comment
Большое спасибо за разъяснения. У меня никогда не было ссылок класса на статически созданные объекты в качестве переменных-членов (только указатели (или значения...)). Это работает по желанию и чище, чем разыменование и новое/удаление. - person JohnML; 04.07.2013
comment
Пожалуйста. Если вы еще не знакомы с темами жизни и собственности, я предлагаю вам начать копаться в этом. Например, вы можете использовать std::vector вместо new/delete для char буферов. В других случаях std::unique_ptr и std::shared_ptr следует использовать, если это возможно, когда вы используете new для создания объекта. На SO есть тонны ресурсов по этому поводу;) - person Captain Obvlious; 04.07.2013
comment
Я пропустил вставку удалений в исходном сообщении (обновлено сейчас). Я знаком с общими указателями и владением объектами, но еще не перешел на C++11, поэтому пока не могу в полной мере воспользоваться некоторыми функциями. Хотя я неплохо разобрался с valgrind ;) - person JohnML; 04.07.2013