Как передать функцию в функцию? - PullRequest
3 голосов
/ 10 июня 2010

Это странный заголовок.Я был бы очень признателен, если бы кто-то мог уточнить, что именно я спрашиваю, потому что я сам в этом не уверен.

Я смотрю Стэнфордские видеофильмы о парадигмах программирования (этот учитель потрясающий), и явплоть до пятого видео, когда он начал это делать:

void *lSearch( void* key, void* base, int elemSize, int n, int (*cmpFn)(void*, void*))

Естественно, я подумал про себя: «Ой, я не знал, что вы могли бы объявить функцию и определить ее позже!».Поэтому я создал свою собственную тестовую версию C ++.

int foo(int (*bar)(void*, void*));
int bar(void* a, void* b);

int main(int argc, char** argv)
{
    int *func = 0;
    foo(bar);

    cin.get();
    return 0;
}

int foo(int (*bar)(void*, void*))
{
    int c(10), d(15);
    int *a = &c;
    int *b = &d;
    bar(a, b);
    return 0;
}

int bar(void* a, void* b)
{
    cout << "Why hello there." << endl;
    return 0;
}

Вопрос о коде заключается в следующем: он провалится, если я объявлю функцию int *bar в качестве параметра foo, но не int (*bar).Зачем!?

Кроме того, видео смущает меня тем, что его определение lSearch

void* lSearch( /*params*/ , int (*cmpFn)(void*, void*)) вызывает cmpFn в определении, но при вызове функции lSearch

lSearch( /*params*/, intCmp );

также вызывает определенную функцию int intCmp(void* elem1, void* elem2);, и я не понимаю, как это работает.Почему в lSearch функция называется cmpFn, но определяется как intCmp, которая имеет тип int, а не int* и все еще работает?И почему функция в lSearch не должна иметь определенные параметры?

Ответы [ 4 ]

10 голосов
/ 10 июня 2010

int (*cmpFn)(void*, void*) - это указатель на функцию - указатель на функцию, которую можно вызвать позже. Когда вы вызываете iSearch, вы передаете ему функцию, которая принимает два void* и возвращает int, и это связывает это с параметром cmpFn. Тогда iSearch может сделать что-то вроде int x = cmpFn(voidPtr1, voidPtr2); для вызова этой функции, передав ей voidPtr1 и voidPtr2 в качестве аргументов и сохраняя возвращаемое значение в x

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

int test1(int x) {return x;}
int test2(int x) {return x+1;}

int main(int argc, char** argv) {
    int (*fn)(int); // Function pointer named 'fn' that can hold a function that takes one int argument and returns an int
    int rtn;

    fn = test1; // Assign the 'test1' function to 'fn'
    rtn = fn(4); // Call 'fn' ('test1') with the argument 4; it returns 4 and stores it in 'rtn'

    fn = test2; // Assign the 'test2' function to 'fn'
    rtn = fn(4); // Call 'fn' ('test2') with the argument 4; it returns 5 and stores it in 'rtn'
}

Сбой, если вы объявите int *bar, потому что это не указатель на функцию, это просто указатель на целое число. Синтаксис для указателей на функции: rtn_type (*name)(param1_type, param2_type, ...)

4 голосов
/ 10 июня 2010

Вопрос о коде заключается в следующем: произойдет сбой, если я объявлю функцию int * bar в качестве параметра foo, но не int (* bar).Зачем!?

Поскольку вы бы объявили указатель функции как указатель на функцию, возвращающую int*.Обратите внимание, что в списках параметров нет разницы между объявлением указателя функции и функции, а также нет различий между объявлением массива и указателя:

void f(void a());
void f(void(*a)());

void g(int a[]);
void g(int *a);

Это эквивалентно - первые два имеютих параметры имеют типы указателей на функции, а вторые два имеют свои параметры типа указателя.

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

Почему в lSearch функция называется cmpFn, но определяется как intCmp, которая имеет тип int, а не int * и все еще работает?

Звезда означаетчто это указатель на функцию.Звезда не привязывается к возвращаемому типу (именно поэтому, в первую очередь, присутствует ограничение - иметь привязку к функции, а не к возвращаемому типу).

И почему функция в lSearch не должна иметь определенные параметры?

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

2 голосов
/ 10 июня 2010

Существует огромное количество учебных пособий , посвященных указателям на функции.

Ответом на ваш первый вопрос является ассоциативность operator*, которую он связывает с типом первым.

Во втором примере lSearch принимает функцию, которая переводит void* в int.Именно так объявляется intCmp.

В любом случае: указатели на функции не так важны в C ++.Они должны быть обернуты в Functors (структуры с определением operator()), и вы должны использовать шаблоны вместо void*.

1 голос
/ 10 июня 2010

Синтаксис:

int (*cmpFn)(void*, void*)

Означает «указатель на функцию, которая принимает два параметра void * и возвращает int».Размещение скобок - это просто часть синтаксиса.Я подозреваю, что они необходимы для устранения неоднозначности в случае, когда у вас есть функция, которая возвращает int* против int:

int* (*cmpFn)(void*, void*)

против

// Ambiguous - could be a pointer to a pointer to a function that returns int, or a pointer
// to a function that returns int*
int* *cmpFn(void*,void*) 

Что касается соглашений о вызовах, представьте cmpFn как переменную, которая содержит функцию.Имя cmpFn - это имя этой переменной внутри функции, но само имя функции может отличаться, как с обычной переменной:

void foo(int x) {
   // Variable is called "x" inside the function
}

void main() {
   int blah = 1;
   foo(blah);  // But it's called "blah" here
}

И с функцией:

void myfunction(void *ptr) {
   // Does something.
}

void call_a_function( void (*func)(void*) ) {
   void* someptr;
   func(someptr); // Function is called "func" here.
}

void main() {
  call_a_function(myfunction); // But it's called "myfunction" here.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...