Атрибуты-члены, которые являются функциями в C ++ - PullRequest
1 голос
/ 26 марта 2012

Я пытаюсь научить себя немного C ++ и в качестве начального проекта я хочу создать код для выполнения метода Ньютона над функциями одной переменной.Я хочу создать класс ObjectiveFunction, в котором будут храниться пользовательские функции для целевой функции, первой производной и второй производной.

Я хочу, чтобы конструктор ObjectiveFunction принимал от 0 до 3 аргументовгде аргументы сами по себе являются функциями:

// ObjectiveFunction.h
// Class definition for an Objective Function object.

#ifndef OBJECTIVE_H
#define OBJECTIVE_H

class ObjectiveFunction
{
    public:
        // Constructors

        // Default constructor
        ObjectiveFunction();

        // Constructor with just objective function.
       ObjectiveFunction(double f(double));

       // Constructor with objective function and derivative function.
       ObjectiveFunction(double f(double), double fp(double));

       // Constructor with objective, derivative, and second derivative functions.
       ObjectiveFunction(double f(double), double fp(double), double fpp(double));

       // Methods
       void setObjectiveFunction(double f(dobule));
       void setDerivativeFunction(double f(double));
       void setSecondDerivativeFunction(double f(double));

       double evalObjectiveFunction(double);
       double evalDerivativeFunction(double);
       double evalSecondDerivativeFunction(double);

    private:
       // Storage for the functions.
       // This is the part I'm not sure of.

       // Attempt with function pointers
       double (*objFunc)(double);
       double (*d1Func)(double);
       double (*d2Func)(double);

};

#endif // OBJECTIVE_H

Как бы я создал закрытые элементы данных, которые сами являются функциями.Я хочу создать объекты функций, которые (кроме закрытых) были бы доступны, например, foo.obj_func(3.0) или foo.deriv_func(3.0), где obj_func устанавливается конструктором на основе функций, которые пользователь передает классу.

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

Ответы [ 4 ]

3 голосов
/ 26 марта 2012

Используйте std::function<double(double)>.Это позволит вам легко сохранять все типы функций.

0 голосов
/ 17 февраля 2016
#include <stdio.h>

class MyClass
{
public:

  typedef int(MyClass::*FunctionPointer)(int);

  FunctionPointer func;

  void Setup(int selector)
  {
        switch (selector)
        {
              case 1:
              {
                    this->func = (FunctionPointer)&MyClass::Test1;
                    break;
              }
              case 2:
              {
                    this->func = (FunctionPointer)&MyClass::Test2;
                    break;
              }
        }
  }

  void Call()
  {
        (this->*func)(10);
  }

  void Test1(int a)
  {
        printf("test1\n");
  }
  void Test2(int a)
  {
        printf("test2\n");
  }
};

int main()
{
  MyClass *instance = new MyClass();

  instance->Setup(1);
  instance->Call();
  instance->Setup(2);
  instance->Call();

  return 0;
}
0 голосов
/ 26 марта 2012

Функциональные указатели будут работать или, альтернативно, если вы просто хотите разрешить несколько реализаций без изменения вызывающего кода, вы можете использовать наследование.

Посмотрите здесь длясравнение указателей функций и методов наследования.

Решение по наследованию будет выглядеть примерно так;Сначала создайте базовый класс, который реализует интерфейс, который вы хотите:

class BaseObjectiveFunction
{
    BaseObjectiveFunction();
    virtual ~BaseObjectiveFunction();

    virtual double EvalObjectiveFunction(double);
    virtual double EvalDerivativeFunction(double);
    virtual double EvalSecondDerivativeFunction(double);
}

Затем реализуйте унаследованный класс, который имеет желаемую функциональность:

class LogLikelihood: public BaseObjectiveFunction
{
    public:
        LogLikelihood();
        ~LogLikelihood();

        virtual double EvalObjectiveFunction(double);
        virtual double EvalDerivativeFunction(double);
        virtual double EvalSecondDerivativeFunction(double);

        // Example of a function to pass in some data which the calculations rely on
        bool SetInputData(double* somedata, int dataLen);
}

Наконец, чтобы вызвать этот код, который вы создаетеуказатель на ваш базовый класс и указание на реализацию, которая вас интересует (аналогично тому, как вы передавали бы указатели на функции):

LogLikelihood* pOF1 = new LogLikelihood(); // Create an instantiation of a specific solver
BaseObjectiveFunction* pCaller = pOF1; // Establish a pointer to the interface class and at the specific implementation.

// Now whatever calls the evaluate function can just use the pCaller and doesn't have to worry about the actual implementation.
pCaller->EvalObjectiveFunction(1.5);

Это вызовет две базовые реализации, которые (Iподумайте) что вы действительно хотите здесь.

0 голосов
/ 26 марта 2012

У вас есть несколько вариантов, чтобы сделать здесь, прежде чем выбрать метод реализации ....

После создания ObjectiveFunction вам понадобится способ изменить функции после того, как они были установлены?Вы хотите быть более гибкими в отношении того, что вы принимаете в качестве «функции» (т. Е. Будут ли приемлемы «функторы» (в основном все, что реализует оператор вызова функции))?Вы хотите, чтобы вещи, использующие ObjectiveFunction, могли использовать любые взаимозаменяемые ObjectiveFunction независимо от того, какие функции были заданы в качестве аргумента при его создании?

Вы выбрали очень динамичный подход.И единственный выбор, который вам действительно нужно сделать, - использовать ли вы указатель функции или объект ::std::function.Объект ::std::function может ссылаться на все, что реализует оператор вызова функции (например, результат ::std::bind).Указатель на функцию может ссылаться только на простую старую функцию.

Лично я бы исследовал наличие шаблонизированной ObjectiveFunction.Частично потому, что тогда вызываемые им функции будут установлены во время компиляции, и компилятор сможет лучше оптимизировать их.С техникой поиска корней вы будете часто их называть.Так что в некоторых случаях это может значительно ускорить процесс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...