Почему указатели на функции не считаются объектно-ориентированными? - PullRequest
23 голосов
/ 20 апреля 2011

В спецификациях языка C # прямо говорится:

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

Я понимаю, что делегаты должны быть немного более гибкими, чем указатели, потому что .NET перемещает память. Это единственное различие, о котором я знаю, но я не уверен, как это превратит делегата в концепцию ОО ...?

Что делает указатель на функцию не объектно-ориентированным? Указатели и указатели функций эквивалентны?

Ответы [ 6 ]

32 голосов
/ 20 апреля 2011

Что ж, в Википедии сказано, что «объектно-ориентированный» означает использование «таких функций, как абстракция данных, инкапсуляция, обмен сообщениями, модульность, полиморфизм и наследование».Не имея лучшего определения, давайте пойдем с этим.

Указатели функций не содержат данных, они не инкапсулируют детали реализации, они не отправляют и не принимают сообщения, они не являются модульными, они обычно не используются вполиморфным образом (хотя я полагаю, что теоретически они могут быть ковариантными и контравариантными в своих типах возвращаемых и формальных параметров, как делегаты сейчас находятся в C # 4), и они не участвуют в иерархии наследования.Они не описывают себя;Вы не можете запросить указатель на функцию для ее типа, потому что он не имеет указателя.

Напротив, делегаты собирают данные - они держатся за приемник.Они поддерживают обмен сообщениями в том смысле, что вы можете «отправить сообщение» делегату, вызвав его методы ToString, GetType, Invoke или BeginInvoke, чтобы сказать ему что-то сделать, и он «сообщит» вам результат.Типы делегатов могут быть ограничены определенными доменами доступности, если вы решите это сделать.Они являются объектами с самоописанием, которые имеют метаданные и во время выполнения знают свой собственный тип.Они могут быть объединены с другими делегатами.Они могут использоваться полиморфно как System.MulticastDelegate или System.Delegate, типы, от которых они наследуются.И их можно использовать полиморфно в том смысле, что в C # 4 типы делегатов могут быть ковариантными и контравариантными в своих типах возвращаемых значений и параметров.

2 голосов
/ 20 апреля 2011

Я полагаю, это потому, что, когда вы удерживаете делегат в методе-члене, OO-структура "знает", что вы держите ссылку на объект-держатель, тогда как в случае с указателями на функцию, во-первых, функция не обязательно является членом Метод и, во-вторых, если функция является методом-членом, OO-инфраструктура не знает, что она должна препятствовать освобождению объекта-владельца.

1 голос
/ 20 апреля 2011

Я объясню на примерах C ++, потому что это язык, где эта проблема присутствует (и решена другим способом).

Простой указатель функции просто содержит адрес функции, ничего больше.

Рассмотрим функцию

void f(int x) { return; }

Теперь простой указатель функции объявляется и присваивается следующим образом:this:

void (*fptr)(int) = &f;

И вы можете использовать это просто:

foo(5); // calls f(5)

Однако в объектно-ориентированном языке мы обычно имеем дело с функциями-членами, а не со свободными функциями.И вот тут все становится неприятно.Рассмотрим следующий класс:

class C { void g(int x) { return; } };

Объявление указателя функции на C :: g выполняется следующим образом:

void (*C::gptr)(int) = &C::g;

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

По той же причине вызывать их проблематично.Этот параметр this нуждается в значении, что означает, что вам нужен экземпляр.Вызов указателя на функцию-член выполняется следующим образом:

C c;
(c.*gptr)(5);  // calls c.g(5);

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

Очевидная идея состоит в том, чтобы заключить в капсулу два, и это то, что делегат.Вот почему делегат считается более ООП.Я понятия не имею, почему он считается более безопасным для типов (возможно, потому что вы можете привести указатели функций к void *).

Кстати, решение C ++ в C ++ 0x взято из Boost.Он называется std::function и std::bind и работает так:

std::function<void (C*, int)> d = std::bind(&c::g, &c);
d(5); // calls c.g(5);
1 голос
/ 20 апреля 2011

Указатели функций - это просто адреса памяти.

Делегаты - это объекты, которые имеют методы и свойства:
-BeginInvoke
-DynamicInvoke
-Invoke
-Метод
-Target
и т.д.

0 голосов
/ 23 сентября 2014

Предположим, что кто-то хочет разработать метод anyprintf общего назначения, который может вести себя как fprintf, sprintf, cprintf [console printf с поддержкой цвета].Один из подходов заключается в том, чтобы он принимал функцию, которая принимает void* и char вместе с void* и va_list;тогда для каждого символа вывода следует вызывать переданную функцию, передавая ей предоставленный указатель и символ для вывода.

При наличии такой функции можно реализовать vsprintf и fprintf [игнорируяих значения возвращаются для упрощенно] через:

void fprint_function(void* data, char ch) { fputc( (FILE*)data, ch); }
void sprint_function(void* data, char ch) { char**p = (char**)data; *((*p)++) = ch; }
void fprint_function(void* data, char ch) { cputchar( ch); }
void vfprintf(FILE *f, va_list vp, const char *fmt, va_list vp)
{
  vsanyprintf(fprint_function, (void*)f, st, vp);
}
void vsprintf(char *st, va_list vp, const char *fmt, va_list vp)
{
  vsanyprintf(fprint_function, (void*)f, st, vp);
}
void vcprintf(va_list vp, const char *fmt, va_list vp)
{
  vsanyprintf(cprint_function, (void*)0, st, vp);
}

Эффективно, комбинация указателя функции и void* ведут себя как метод.К сожалению, компилятор не может гарантировать, что данные, передаваемые в void*, будут иметь форму, ожидаемую предоставленной функцией.C ++ и другой объектно-ориентированный язык добавляют во время компиляции проверку соответствия такого типа.

0 голосов
/ 20 апреля 2011

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

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