Mam problem ze zrozumieniem użyteczności wskaźników funkcji. Myślę, że w niektórych przypadkach może się to przydać (w końcu istnieją), ale nie przychodzi mi do głowy przypadek, w którym lepiej lub nieuniknione jest użycie wskaźnika funkcji.
Czy możesz podać przykład dobrego wykorzystania wskaźników funkcji (w C lub C ++)?
Odpowiedzi:
Większość przykładów sprowadza się do wywołań zwrotnych : wywołujesz funkcję
f()
przekazującą adres innej funkcjig()
if()
wywołujeg()
określone zadanie. Jeśli zamiast tego podaszf()
adresh()
,f()
oddzwonięh()
.Zasadniczo jest to sposób parametryzacji funkcji: pewna część jej zachowania nie jest zakodowana na stałe
f()
, ale w funkcji wywołania zwrotnego. Osoby wywołujące mogąf()
zachowywać się inaczej, przekazując różne funkcje zwrotne. Klasyk pochodziqsort()
z biblioteki standardowej C, która przyjmuje kryterium sortowania jako wskaźnik do funkcji porównawczej.W C ++ robi się to często za pomocą obiektów funkcji (zwanych również funktorami). Są to obiekty, które przeciążają operatora wywołania funkcji, więc można je wywoływać tak, jakby były funkcją. Przykład:
Pomysł polega na tym, że w przeciwieństwie do wskaźnika funkcji obiekt funkcji może przenosić nie tylko algorytm, ale także dane:
Inną zaletą jest to, że czasami łatwiej jest wywołać wbudowane wywołania do obiektów funkcji niż wywołania za pośrednictwem wskaźników funkcji. To jest powód, dla którego sortowanie w C ++ jest czasami szybsze niż sortowanie w C.
źródło
Cóż, generalnie używam ich (profesjonalnie) w tabelach skoków (zobacz także to pytanie StackOverflow ).
Tabele skoków są powszechnie (ale nie wyłącznie) używane w maszynach o skończonych stanach, aby sterować danymi. Zamiast zagnieżdżonego przełącznika / obudowy
możesz utworzyć tablicę wskaźników funkcji 2D i po prostu wywołać
handleEvent[state][event]
źródło
Przykłady:
źródło
std::sort
„scomp
parametr jako strategia„Klasycznym” przykładem użyteczności wskaźników do funkcji jest
qsort()
funkcja biblioteki C , która implementuje szybkie sortowanie. Aby być uniwersalnym dla wszystkich i wszystkich struktur danych, które użytkownik może wymyślić, potrzeba kilku wskaźników pustej przestrzeni do sortowanych danych i wskaźnika do funkcji, która wie, jak porównać dwa elementy tych struktur danych. To pozwala nam stworzyć naszą wybraną funkcję dla zadania, a nawet pozwala na wybór funkcji porównawczej w czasie wykonywania, np. Do sortowania rosnąco lub malejąco.źródło
Zgadzam się ze wszystkimi powyższymi, plus ... Podczas dynamicznego ładowania biblioteki dll w czasie wykonywania, będziesz potrzebować wskaźników do funkcji, aby wywołać funkcje.
źródło
Mam zamiar pójść tutaj pod prąd.
W języku C wskaźniki funkcji są jedynym sposobem implementacji dostosowywania, ponieważ nie ma OO.
W C ++ możesz użyć wskaźników funkcji lub funktorów (obiektów funkcji) dla tego samego wyniku.
Funktory mają wiele zalet w porównaniu ze wskaźnikami funkcji surowej, ze względu na ich obiektowy charakter, a mianowicie:
operator()
lambda
ibind
)Osobiście wolę funktory od wskaźników do funkcji (pomimo standardowego kodu), głównie dlatego, że składnia wskaźników do funkcji może łatwo ulec owłosieniu (z samouczka dotyczącego wskaźników funkcji ):
Jedyny raz, kiedy widziałem wskaźniki funkcji używane tam, gdzie funktory nie mogły, był w Boost.Spirit. Całkowicie nadużyli składni, aby przekazać dowolną liczbę parametrów jako pojedynczy parametr szablonu.
Ale ponieważ zmienne szablony i wyrażenia lambda są tuż za rogiem, nie jestem pewien, czy długo będziemy używać wskaźników funkcji w czystym kodzie C ++.
źródło
bind
lubfunction
używasz wskaźników do funkcji. To tak, jakby powiedzieć, że nie używamy wskaźników w C ++, ponieważ używamy inteligentnych wskaźników. Tak czy owak, czepiam się dzioba.W języku C klasycznym zastosowaniem jest funkcja qsort , w której czwartym parametrem jest wskaźnik do funkcji używanej do wykonywania porządkowania w ramach sortowania. W C ++ do tego rodzaju rzeczy można by używać funktorów (obiektów, które wyglądają jak funkcje).
źródło
Ostatnio użyłem wskaźników do funkcji, aby utworzyć warstwę abstrakcji.
Mam program napisany w czystym C, który działa na systemach wbudowanych. Obsługuje wiele wariantów sprzętu. W zależności od sprzętu, na którym pracuję, musi wywoływać różne wersje niektórych funkcji.
W czasie inicjalizacji program ustala, na jakim sprzęcie działa i zapełnia wskaźniki funkcji. Wszystkie procedury wyższego poziomu w programie po prostu wywołują funkcje, do których odwołują się wskaźniki. Mogę dodać obsługę nowych wariantów sprzętu bez dotykania procedur wyższego poziomu.
Kiedyś używałem instrukcji switch / case do wybierania odpowiednich wersji funkcji, ale stało się to niepraktyczne, ponieważ program zaczął obsługiwać coraz więcej wariantów sprzętowych. Wszędzie musiałem dodawać opisy przypadków.
Próbowałem również pośrednich warstw funkcyjnych, aby dowiedzieć się, której funkcji użyć, ale niewiele pomogły. Nadal musiałem aktualizować opisy przypadków w wielu miejscach, gdy dodawaliśmy nowy wariant. Dzięki wskaźnikom funkcji wystarczy zmienić funkcję inicjalizacyjną.
źródło
Podobnie jak Rich powiedział powyżej, bardzo często wskaźniki funkcji w systemie Windows odnoszą się do adresu, który przechowuje funkcje.
Podczas programowania
C language
na platformie Windows w zasadzie ładujesz jakiś plik DLL do pamięci podstawowej (używającLoadLibrary
) i aby korzystać z funkcji przechowywanych w DLL, musisz utworzyć wskaźniki funkcji i wskazać na te adresy (używającGetProcAddress
).Bibliografia:
LoadLibrary
GetProcAddress
źródło
Wskaźniki funkcji mogą być używane w C do tworzenia interfejsu do programowania. W zależności od konkretnej funkcji, która jest potrzebna w czasie wykonywania, do wskaźnika funkcji można przypisać inną implementację.
źródło
Moim głównym zastosowaniem były ODWOŁANIA: kiedy musisz zapisać informacje o funkcji, aby wywołać ją później .
Powiedz, że piszesz Bomberman. 5 sekund po tym, jak osoba upuści bombę, powinna wybuchnąć (wywołać
explode()
funkcję).Teraz są na to dwa sposoby. Jednym ze sposobów jest „sondowanie” wszystkich bomb na ekranie, aby sprawdzić, czy są gotowe do wybuchu w głównej pętli.
Innym sposobem jest dołączenie wywołania zwrotnego do systemu zegarowego. Kiedy bomba jest podłożona, dodajesz wywołanie zwrotne, aby wywołać bomb.explode () w odpowiednim czasie .
Tutaj
callback.function()
może być dowolna funkcja , ponieważ jest to wskaźnik funkcji.źródło
Zastosowanie wskaźnika funkcji
Aby wywołać funkcję dynamicznie na podstawie danych wprowadzonych przez użytkownika. W tym przypadku tworząc mapę łańcucha i wskaźnika funkcji.
W ten sposób użyliśmy wskaźnika funkcji w naszym rzeczywistym kodzie firmy. Możesz wpisać 'n' numer funkcji i wywołać je tą metodą.
WYNIK:
źródło
Z innej perspektywy, oprócz innych dobrych odpowiedzi tutaj:
W C używasz tylko wskaźników do funkcji, a nie (bezpośrednio) funkcji.
To znaczy, piszesz funkcje, ale nie możesz nimi manipulować . Nie ma reprezentacji funkcji jako takiej w czasie wykonywania, której można by użyć. Nie możesz nawet nazwać „funkcji”. Kiedy piszesz:
tak naprawdę mówisz „wykonaj wywołanie
my_function
wskaźnika z określonym argumentem”. Wykonujesz wywołanie za pomocą wskaźnika funkcji. Ten zanik wskaźnika do funkcji oznacza, że następujące polecenia są równoważne z poprzednim wywołaniem funkcji:i tak dalej (dzięki @LuuVinhPhuc).
Więc już używasz wskaźników funkcji jako wartości . Oczywiście chciałbyś mieć zmienne dla tych wartości - i tutaj są wszystkie zastosowania innych metion: polimorfizm / dostosowywanie (jak w qsort), wywołania zwrotne, tabele skoku itp.
W C ++ sprawy są nieco bardziej skomplikowane, ponieważ mamy lambdy i obiekty z klasą,
operator()
a nawet zstd::function
klasą, ale zasada jest nadal taka sama.źródło
(&my_function)(my_arg)
,(*my_function)(my_arg)
,(**my_function)(my_arg)
,(&**my_function)(my_arg)
,(***my_function)(my_arg)
... ponieważ funkcje rozkładowi do wskaźników funkcjiW przypadku języków OO, do wykonywania wywołań polimorficznych za kulisami (myślę, że jest to również ważne dla C do pewnego momentu).
Ponadto są bardzo przydatne do wstrzykiwania innego zachowania do innej funkcji (foo) w czasie wykonywania. To sprawia, że funkcja jest funkcją wyższego rzędu. Poza tym jest to elastyczność, która sprawia, że kod foo jest bardziej czytelny, ponieważ pozwala wyciągnąć z niego dodatkową logikę „if-else”.
Umożliwia wiele innych przydatnych rzeczy w Pythonie, takich jak generatory, zamknięcia itp.
źródło
Używam często wskaźników funkcji do emulacji mikroprocesorów, które mają 1-bajtowe opkody. Tablica 256 wskaźników funkcji jest naturalnym sposobem implementacji tego.
źródło
Jednym z zastosowań wskaźnika funkcji może być sytuacja, w której możemy nie chcieć modyfikować kodu, w którym funkcja jest wywoływana (co oznacza, że wywołanie może być warunkowe iw innych warunkach musimy wykonać inny rodzaj przetwarzania). Tutaj wskaźniki funkcji są bardzo przydatne, ponieważ nie musimy modyfikować kodu w miejscu, w którym funkcja jest wywoływana. Po prostu wywołujemy funkcję za pomocą wskaźnika funkcji z odpowiednimi argumentami. Wskaźnik funkcji może wskazywać warunkowo różne funkcje. (Można to zrobić gdzieś podczas fazy inicjalizacji). Ponadto powyższy model jest bardzo pomocny, jeśli nie jesteśmy w stanie zmodyfikować kodu, w którym jest wywoływany (załóżmy, że jest to biblioteka API, której nie możemy modyfikować). API używa wskaźnika funkcji do wywoływania odpowiedniej funkcji zdefiniowanej przez użytkownika.
źródło
Spróbuję podać tutaj nieco obszerną listę:
Wywołania zwrotne : dostosuj niektóre funkcje (biblioteki) za pomocą kodu dostarczonego przez użytkownika. Przykład Prime jest
qsort()
, ale jest również przydatny do obsługi zdarzeń (takich jak przycisk wywołujący wywołanie zwrotne po kliknięciu) lub niezbędny do rozpoczęcia wątku (pthread_create()
).Polimorfizm : tabela vtable w klasie C ++ to nic innego jak tabela wskaźników funkcji. Program w C może również udostępnić tabelę vtable dla niektórych swoich obiektów:
Konstruktor programu
Derived
ustawiłby następnie swojąvtable
zmienną składową na obiekt globalny z implementacjamidestruct
i klasy pochodnejfrobnicate
, a kod, który musiałby zniszczyć astruct Base*
, po prostubase->vtable->destruct(base)
wywołałby poprawną wersję destruktora, niezależnie od tego, która klasa pochodnabase
faktycznie wskazuje na .Bez wskaźników funkcyjnych polimorfizm musiałby zostać zakodowany za pomocą całej armii konstrukcji przełączających, takich jak
To dość szybko staje się nieporęczne.
Kod ładowany dynamicznie : gdy program ładuje moduł do pamięci i próbuje wywołać swój kod, musi przejść przez wskaźnik funkcji.
Wszystkie zastosowania wskaźników funkcji, które widziałem, mieszczą się w jednej z tych trzech szerokich klas.
źródło