2012-03-07 9 views
12

Właśnie dowiedziałem się, o czym naprawdę jest funkcja std :: i do czego jest ona używana i mam pytanie: skoro w zasadzie mamy delegatów, gdzie i kiedy powinniśmy używać abstrakcyjnych klas bazowych i kiedy zamiast tego powinien implementować polimorfizm przez obiekty std :: function podawane do klasy generycznej? Czy ABC otrzymało śmiertelne uderzenie w C++ 11?Czy powinienem przestać używać abstrakcyjnych klas bazowych/interfejsów i zamiast tego użyć funkcji boost :: function/std ::?

Osobiście moje dotychczasowe doświadczenia polegają na tym, że przełączanie delegatów jest o wiele prostsze w zakodowaniu niż tworzenie wielu dziedziczonych klas dla poszczególnych zachowań ... więc jestem trochę zdezorientowany, jak użyteczne są abstrakcyjne bazy od teraz.

+2

W jaki sposób 'std :: function' różni się od wskaźników funkcyjnych (* w tym kontekście *)? Nie widzę żadnej różnicy. Jeśli tak, to co to ma z C++ 11? Można również użyć wskaźników funkcyjnych do implementacji polimorfizmu runtime w pre-C++ 11. – Nawaz

+0

Nie jestem nawet pewien, jak rozpocząć odpowiadanie ... na przykład funkcja std :: może zawierać wskaźnik do funkcji o innym typie i liczbie argumentów, ponieważ możesz std :: bindować je przed faktycznym przekazaniem do niego. I oczywiście jest to prostsze w użyciu. Jeśli mógłbyś cokolwiek zapytać o to, co może zrobić to, czego nie potrafią faceci ... to pytanie zrozumiałbym. Mimo to, dodanie standardowego i * prostego * sposobu na tworzenie delegatów jest tak czy inaczej przyczyną mojego pytania. – Zeks

+0

"* Nadal, oprócz standardowego i prostego sposobu tworzenia delegatów, to i tak zrodziło się moje pytanie. *" ... Ale to * nie jest prostsze * niż używanie abstrakcyjnych funkcji bazowych i wirtualnych, więc jaki jest sens? – Nawaz

Odpowiedz

19

Prefer dobrze określony interfejs na callbacków

Problem std::function (poprzednio boost::function) jest to, że w większości przypadków trzeba mieć zwrotną metody klasy, a więc trzeba wiązać this do obiektu funkcji. Jednak w kodzie wywołującym nie masz możliwości sprawdzenia, czy nadal jest w pobliżu this. W rzeczywistości nie masz pojęcia, że ​​istnieje nawet this, ponieważ bind ukształtował sygnaturę funkcji wywołującej w to, czego żąda wywołujący.

Może to oczywiście powodować dziwne awarie, ponieważ wywołanie zwrotne próbuje uruchomić metody dla klas, które już nie istnieją.

Możesz, oczywiście, użyć shared_from_this i powiązać shared_ptr do wywołania zwrotnego, ale wtedy twoja instancja może nigdy nie zniknąć. Osoba, która ma do ciebie telefon oddzwaniowy, teraz bierze udział w twoim posiadaniu, nawet nie wiedząc o tym. Prawdopodobnie chcesz bardziej przewidywalne własności i zniszczenia.

Kolejny problem, nawet jeśli można uzyskać oddzwonienie, aby działał poprawnie, jest z wywołania zwrotnego, kod może być zbyt oddzielone od siebie. Relacje między obiektami mogą być tak trudne do stwierdzenia, że ​​czytelność kodu zmniejsza się. Interfejsy zapewniają jednak dobry kompromis między odpowiednim poziomem rozłączenia a jasno określoną relacją, zgodnie z definicją umowy interfejsu. Możesz także bardziej precyzyjnie określić, w tym związku, kwestie, kto jest właścicielem kogo, porządek destrcution itp.

Dodatkowym problemem z std::function jest to, że wiele debuggerów nie obsługuje ich dobrze. W VS2008 i funkcjach doładowania musisz przejść przez około 7 warstw, aby dostać się do swojej funkcji. Nawet jeśli wszystkie inne rzeczy są równe, najlepszym rozwiązaniem był oddzwonienie, zwykła irytacja i czas zmarnowany przypadkowo przekraczanie celu std::function jest wystarczającym powodem, aby tego uniknąć. Dziedziczenie jest podstawową cechą języka, a przejście na nadpisaną metodę interfejsu jest natychmiastowe.

Na koniec dodam tylko , nie mamy delegatów w C++. Delegaci w C# stanowią rdzeń języka, podobnie jak dziedziczenie jest w C++ i C#. Mamy funkcję biblioteki standardowej, którą IMO stanowi jedna warstwa usunięta z funkcji języka podstawowego. Więc nie będzie tak ściśle zintegrowany z innymi podstawowymi funkcjami tego języka. Zamiast tego pomaga sformalizować ideę obiektów funkcyjnych, które od dawna są idiomem C++.

+0

Thx, punkt o "tym" jest ważny, nie brałem tego pod uwagę. Będę o tym pamiętać. – Zeks

+0

@Zeks yup. Kiedyś byłam 'boost :: function' szczęśliwa, ale później żałowałem tego. Jest to funkcja, która ma określone zastosowania, ale IMO nie powinna być domyślną odpowiedzią na problemy. –

+0

Btw, czy jest jakiś artykuł na temat możliwych pułapek przy użyciu std/boost :: function? Czy jest w tym coś więcej niż martwienie się o "to"? – Zeks

1

Nie rozumiem, w jaki sposób można dojść do wniosku, że wskaźniki funkcji sprawiają, że abstrakcyjne klasy podstawowe stają się przestarzałe.

Klasa, zawiera metody i dane, które ją dotyczą.

Wskaźnik funkcji jest wskaźnikiem funkcji. Nie ma pojęcia o obiekcie enkapsulującym, jedynie zna parametry przekazane do niego.

Kiedy piszemy klasy, opisujemy dobrze zdefiniowane obiekty.

Wskaźniki funkcji są dobre dla wielu różnych rzeczy, ale zmiana zachowania obiektu niekoniecznie jest celem docelowym (chociaż przyznaję, że może się zdarzyć, że tak się stanie, np. Wywołania zwrotne, jako Doug.T wspomina).

Proszę nie mylić tych dwóch.

+0

Nie mówię, że są przestarzałe. Tylko ta funkcja std :: może być zastosowana do wykonania nieco podobnej pracy. Pytam o to, ponieważ nie jestem pewien, gdzie są granice między tymi dwoma :) – Zeks

Powiązane problemy