Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > переопределение функции


Автор: gordmiand 25.11.2010, 21:31
Подскажите пож-та каким образом верно определить различную реализацию одной и той же функции в зависимости от значения внешней переменной (parametr)? Проверять значение parametr внутри функции Func1 и в зависимости от его значения выполнять те или другие операторы нежелательно, так как эта функция вызывается сотни раз и каждый раз проверять значение parametr нет смысла – оно одно и тоже, равное заданному в начале программы…
я делаю что-то типа: 
Код

#if (parametr==1)
#define Func1(x)  (2*x*x+5)

#if (parametr==2)
#define Func1(x)  (4*x+1)


будет конечно же ошибка, так как условие для  #if должно быть константным (const), но у меня-то это значение (parametr) считывается из файла в начале программы (то есть параметр  parametr переменен)…как быть?

Автор: mes 25.11.2010, 21:38
Цитата(gordmiand @  25.11.2010,  20:31 Найти цитируемый пост)
 но у меня-то это значение (parametr) считывается из файла в начале программы (

язык си или с++ ?

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

Автор: gordmiand 25.11.2010, 21:47
язык С++ - пишу в  Qt 4.6....
то есть только так:
 
Код

int Func1(int x, int parametr)
{  
 if (parametr==1)  return 2*x*x+5;
 else return 4*x+1;
}

что , опять же, нежелательно...
или что Вы имели ввиду? подскажите программно пож-та...

Автор: Cheloveck 25.11.2010, 21:49
Используй функторы
Код

#include <iostream>

class IFunctor
{
public:
    virtual ~IFunctor() { }
    virtual int operator () (int x) = 0;
};

class CFunctorOne :
    public IFunctor
{
public:
    virtual ~CFunctorOne() { }
    
    virtual int operator () (int x)
    {
        return 2 * x * x + 5;
    }
};

class CFunctorTwo :
    public IFunctor
{
public:
    virtual ~CFunctorTwo() { }
    
    virtual int operator () (int x)
    {
        return 4 * x + 1;
    }
};

void MegaFunction(int var)
{
    IFunctor * functor = NULL;

    switch(var)
    {
    case 1:
        functor = new CFunctorOne();
        break;
    case 2:
        functor = new CFunctorTwo();
        break;
    default:
        return;
    }

    for(int i = 0; i < 5; ++i)
    {
        std::cout << (*functor)(i + 100) << " ";
    }
    std::cout << std::endl;
    delete functor;
}

int main()
{
    MegaFunction(1);
    MegaFunction(2);
    return 0;
}


Для создания функтора можно использовать паттерн Абстрактная фабрика

Автор: gordmiand 25.11.2010, 21:54
Cheloveck: это явно не самый простой способ - функторы, дополнительные классы для каждой функции...надо как-то проще...гораздо проще...

Автор: Cheloveck 25.11.2010, 22:09
Цитата(gordmiand @  25.11.2010,  21:54 Найти цитируемый пост)
надо как-то проще...гораздо проще... 

тогда указатели...
Код

#include <iostream>

class CProcess
{
    typedef int (CProcess::*FooType)(int x);
public:
    CProcess(int var)
    {
        switch(var)
        {
        case 1:
            m_function = &CProcess::Foo1;
            break;
        case 2:
            m_function = &CProcess::Foo2;
            break;
        default:
            m_function = &CProcess::Stub;
            break;
        }
    }

    int Run(int x)
    {
        return (this->*m_function)(x);
    }

private:
    int Foo1(int x)
    {
        return 2 * x * x + 5;
    }

    int Foo2(int x)
    {
        return 4 * x + 1;
    }

    int Stub(int)
    {
        return 0;
    }

private:
    FooType m_function;
};

void MegaFunction(int var)
{
    CProcess process(var);
    for(int i = 0; i < 5; ++i)
    {
        std::cout << process.Run(i + 100) << " ";
    }
    std::cout << std::endl;
}

int main()
{
    MegaFunction(1);
    MegaFunction(2);
    return 0;
}

Автор: gordmiand 25.11.2010, 22:40
а почему так не работает:
Код

 
  parametr = 2;


     if (parametr==1)
       #define Flag


              #ifdef Flag
                          #define Func1(x)  ( x*x+12 )
              #else   #define Func1(x)  ( (x+5)/(x-2) )

              #endif


      Edit1->Text= Func1(2);

если parametr = 1; то все работает, при parametr = 2 (например); не срабатывает   #else   #define Func1(x)  ( x ) - ошибок нет, но в эдит ничего не выводится.... почему и как это испрaвить?

Автор: Cheloveck 25.11.2010, 22:50
Это то, что препроцессор передаёт компилятору
Код

$ g++ -E test.cpp
# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.cpp"
  parametr = 2;
test.cpp:6:23: warning: extra tokens at end of #else directive
     if (parametr==1)





      Edit1->Text= ( 2*2 +12 );



Добавлено через 2 минуты и 8 секунд
Почитай про препроцессор. 
Такого
Цитата(gordmiand @  25.11.2010,  22:40 Найти цитируемый пост)
 #else   #define Func1(x)  ( (x+5)/(x-2) )

делать вообще нельзя.
Препроцессор не работает в рантайме, только при компиляции.

Автор: gordmiand 25.11.2010, 23:07
Cheloveck: почему нельзя делать 
Код

#else   #define Func1(x)  ( (x+5)/(x-2) )
???
почему же тогда
Код

  #ifdef Flag
    #define Func1(x)  ( x*x+12 )
      #else
         #define Func1(x)  ( (x+5)/(x-2) )
  #endif

отлично и верно сработает #else...но это если РУКАМИ закомментировать #define Flag - весь вопрос и состоит в том как программно задавать/не задавать #define Flag...
в любом случае Вашими способами очень сложно...мой данный пример можно как-то подкрутить до верного?

Автор: mes 25.11.2010, 23:15
Код

 int fn1 (int x) { return x+1; }
 int fn2 (int x) { return x*2; }

 int (*pfn)(int) = parameter? fn1 : fn2;

 std::cout << pfn(4);

Автор: gordmiand 25.11.2010, 23:23
mes : первые 2 строки понятны - определение функций fn1 и fn2... а вот что делается в третьей строке? когда вызывается функция fn1, а когда - fn2? поясните пож-та....

Автор: mes 25.11.2010, 23:30
если параметр == true (!=0) то указатель pfn инициализируется адресом fn1, иначе  fn2;
в последний строчке идет вывод в консоль результата функции..

Добавлено @ 23:31
Цитата(mes @  25.11.2010,  22:15 Найти цитируемый пост)
int (*pfn)(int) = parameter? fn1 : fn2;

можно переписать так
Код

int (*pfn)(int);
if (parameter) pfn = fn1; else pfn = fn2;

Автор: Cheloveck 26.11.2010, 10:18
gordmiand, способ mesа такой же, как мой последний, только требует глобальной переменной. Объекты же сами хранят своё состояние, а ведь именно это вам и надо. Если это для вас сложно, может не стоит писать на C++?

Цитата(gordmiand @  25.11.2010,  23:07 Найти цитируемый пост)
Cheloveck: почему нельзя делать 
Код

#else   #define Func1(x)  ( (x+5)/(x-2) )

Потому, что директивы препроцессора должны начинаться с символа #, и их (директив) не должно быть более одного на строку.

Автор: mes 26.11.2010, 10:47
Цитата(Cheloveck @  26.11.2010,  09:18 Найти цитируемый пост)
только требует глобальной переменной. 

 smile  smile 

Автор: Cheloveck 26.11.2010, 11:00
Цитата(mes @  26.11.2010,  10:47 Найти цитируемый пост)
 smile  smile 


Цитата(mes @  25.11.2010,  23:15 Найти цитируемый пост)
int (*pfn)(int) = parameter? fn1 : fn2;

Это либо то, что ТС хочет избежать, либо глобальная переменная, либо дополнительный параметр.

Автор: mes 26.11.2010, 11:23
Цитата(Cheloveck @  26.11.2010,  09:18 Найти цитируемый пост)
как мой последний, только требует глобальной переменной.

Цитата(Cheloveck @  26.11.2010,  10:00 Найти цитируемый пост)
Это либо то, что ТС хочет избежать, либо глобальная переменная, либо дополнительный параметр. 


последний пример, это этот ? :
Код

#include <iostream>
class CProcess
{
    typedef int (CProcess::*FooType)(int x);
public:
    CProcess(int var)
    {
        switch(var)
        {
        case 1:
            m_function = &CProcess::Foo1;
            break;
        case 2:
            m_function = &CProcess::Foo2;
            break;
        default:
            m_function = &CProcess::Stub;
            break;
        }
    }
    int Run(int x)
    {
        return (this->*m_function)(x);
    }
private:
    int Foo1(int x)
    {
        return 2 * x * x + 5;
    }
    int Foo2(int x)
    {
        return 4 * x + 1;
    }
    int Stub(int)
    {
        return 0;
    }
private:
    FooType m_function;
};
void MegaFunction(int var)
{
    CProcess process(var);
    for(int i = 0; i < 5; ++i)
    {
        std::cout << process.Run(i + 100) << " ";
    }
    std::cout << std::endl;
}
int main()
{
    MegaFunction(1);
    MegaFunction(2);
    return 0;
}

не могли бы вы уточнить где преимущества и зачем столько лишнего ?

Автор: mes 26.11.2010, 11:40
ладно не буду ждать ответа.. взял за основу вашу мегафункцию - сравнивайте :
Код

void MegaFunction(int var)
{
   struct L
   {
      static int f1(int x) { return 2 * x * x + 5;  }
      static int f2(int x) { return 4 * x + 1;      }
   };
   
   int (&fn)(int x) = var? L::f1 : L::f2;
   
   for(int i = 0; i < 5; ++i)
   {
        std::cout << fn(i + 100) << " ";
   }
   std::cout << std::endl;
}



Автор: Cheloveck 26.11.2010, 11:46
mes, если будет 50 подобных функций, вам нужно будет 50 раз писать
Код

   struct L
   {
      static int f1(int x) { return 2 * x * x + 5;  }
      static int f2(int x) { return 4 * x + 1;      }
   };
   
   int (&fn)(int x) = var? L::f1 : L::f2;

Или передавать уже созданный указатель повсюду, а объект класса можно создать где угодно, и он будет работать сразу.
Конструктор класса легко может выкинуть исключение, если параметр не тот, что ожидается. 

Автор: mes 26.11.2010, 11:54
Цитата(Cheloveck @  26.11.2010,  10:46 Найти цитируемый пост)
 а объект класса можно создать где угодно, и

для получения объекта нужен все равно параметр.. для получения функции он же..

Цитата(Cheloveck @  26.11.2010,  10:46 Найти цитируемый пост)
 если будет 50 подобных функций, вам нужно будет 50 раз писать

достаточно вынести из функции :
Код

namespace A {

typedef int (*fn_t)(int x);
int f1(int x) { return 2 * x * x + 5;  }
int f2(int x) { return 4 * x + 1;      }

fn_t get_fn(int var) { return var? f1 : f2; }

};

void MegaFunction(int var)
{
   A::fn_t fn = A::get_fn(var);
      
   for(int i = 0; i < 5; ++i)
   {
        std::cout << fn(i + 100) << " ";
   }
   std::cout << std::endl;
}

Автор: Cheloveck 26.11.2010, 11:56
Цитата(mes @  26.11.2010,  11:54 Найти цитируемый пост)
достаточно вынести из функции 

И, теперь, если придать этому законченный вид, получится мой пример.

Автор: mes 26.11.2010, 11:57
оба эти варианта, (посредством функтора и функции)  требуют одного и того же..
но у функтора есть некоторые затраты, однако и преимущества связанные с хранением контекста..
но так как контекст у нас не используется, то зачем платить больше ?
тем более что для тс, имхо, с функциями будет полегче..

Добавлено через 8 минут и 58 секунд
Цитата(Cheloveck @  26.11.2010,  10:56 Найти цитируемый пост)
И, теперь, если придать этому законченный вид, получится мой пример. 

не совсем :
Код

struct F
{
   F(int var) : fn (var ? F::f1 : F::f2 ) {}
 
   int operator() (int x) { return fn(x); }
 
 protected:
   static int f1(int x) { return 2 * x * x + 5;  }
   static int f2(int x) { return 4 * x + 1;      }
 private:
   int (&fn)(int);    
};

void MegaFunction(int var)
{
   F fn(1);
      
   for(int i = 0; i < 5; ++i)
   {
        std::cout << fn(i + 100) << " ";
   }
   std::cout << std::endl;
}


Добавлено через 10 минут и 30 секунд
но вопрос был не в схожести, а в том что
Цитата(Cheloveck @  26.11.2010,  09:18 Найти цитируемый пост)
 только требует глобальной переменной

 smile 

Автор: Леопольд 26.11.2010, 14:43
Цитата(gordmiand @  25.11.2010,  21:31 Найти цитируемый пост)
будет конечно же ошибка, так как условие для  #if должно быть константным (const)
В корне неверно. Препроцессор не работает с константами. Он выполняет макроподстановки - копипаст. Можно "задефайнить" макрос (#define MACRO_NAME), и проверять что он  "задефайнен" "#ifdef  MACRO_NAME" и т.д. . Но всё это выполняет препроцессор. Когда препроцессор закончит, в коде не останется ни одного макроса. Ради интереса, поищи флаг компилятора, который выдаёт код после обработки препроцессором.
const - обрабатывается не препроцессором, а компилятором. Этот модификатор призван защитить переменную от изменений.
Цитата(gordmiand @  25.11.2010,  21:31 Найти цитируемый пост)
 но у меня-то это значение (parametr) считывается из файла в начале программы
Эта работа, которая никакого отношения к препроцессору иметь не может. Считываться из файла может только в рантайме (когда программа исполняется CPU). Значит и обрабатывать надо в рантайме. Т.е. после того как "вычислил" значение переменной в соответсвии с ним, что-то сделать, например, настроить глобальный указатель на нужную функцию. Которая потом всегда будет вызываться через него. А функцию, спрятать в другом cpp файле (единице трансляции), если чистый С, то с модификатором static, что-бы её нельзя было вызвать напрямую (в С++ для этих целей есть безымянные пространства имён - unnamed namespaces).

http://forum.vingrad.ru/forum/topic-270683/kw-compiler-linker-preprocessor.html
http://forum.vingrad.ru/forum/topic-269794/kw-faq-c++.html

Автор: Cheloveck 26.11.2010, 18:06
Цитата(Леопольд @  26.11.2010,  14:43 Найти цитируемый пост)
Ради интереса, поищи флаг компилятора, который выдаёт код после обработки препроцессором.

http://forum.vingrad.ru/index.php?showtopic=316068&view=findpost&p=2254657

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)