Czy w C ++ można mieć funkcję składową, która jest jednocześnie static
i virtual
? Wygląda na to, że nie ma na to prostego sposobu ( static virtual member();
jest to błąd kompilacji), ale czy istnieje przynajmniej sposób na osiągnięcie tego samego efektu?
TO ZNACZY:
struct Object
{
struct TypeInformation;
static virtual const TypeInformation &GetTypeInformation() const;
};
struct SomeObject : public Object
{
static virtual const TypeInformation &GetTypeInformation() const;
};
Sensowne jest używanie GetTypeInformation()
zarówno instancji ( object->GetTypeInformation()
), jak i klasy ( SomeObject::GetTypeInformation()
), co może być przydatne do porównań i istotne dla szablonów.
Jedyne sposoby, które przychodzą mi do głowy, obejmują pisanie dwóch funkcji / funkcji i stałej, na klasę lub użycie makr.
Jakieś inne rozwiązania?
const
sygnatura metody in a oznacza niejawnythis
wskaźnik jako stały i nie może być stosowana do metod statycznych, ponieważ nie mają one niejawnego parametru.Odpowiedzi:
Nie, nie da się tego zrobić, bo co się stanie, gdy zadzwonisz
Object::GetTypeInformation()
? Nie może wiedzieć, którą wersję klasy pochodnej ma wywołać, ponieważ nie ma z nią skojarzonego obiektu.Będziesz musiał uczynić z niej niestatyczną funkcję wirtualną, aby działała poprawnie; jeśli chcesz mieć również możliwość nie wirtualnego wywoływania wersji określonej klasy pochodnej bez instancji obiektu, musisz również zapewnić drugą, nadmiarową, statyczną, niewirtualną wersję.
źródło
Wielu twierdzi, że nie jest to możliwe, poszedłbym o krok dalej i powiedziałbym, że nie ma to sensu.
Statyczny element członkowski to coś, co nie odnosi się do żadnej instancji, tylko do klasy.
Wirtualny element członkowski to coś, co nie odnosi się bezpośrednio do żadnej klasy, tylko do instancji.
Zatem statyczny wirtualny element członkowski byłby czymś, co nie odnosi się do żadnej instancji ani żadnej klasy.
źródło
static virtual
metody, alestatic
czystavirtual
metoda ma duże znaczenie w interfejsie.static const string MyClassSillyAdditionalName
.Któregoś dnia napotkałem ten problem: miałem kilka klas pełnych metod statycznych, ale chciałem użyć dziedziczenia i metod wirtualnych oraz zredukować powtarzalność kodu. Moje rozwiązanie brzmiało:
Zamiast używać metod statycznych, użyj singletona z metodami wirtualnymi.
Innymi słowy, każda klasa powinna zawierać statyczną metodę, którą wywołujesz, aby uzyskać wskaźnik do pojedynczej, współużytkowanej instancji klasy. Możesz ustawić prawdziwe konstruktory jako prywatne lub chronione, aby kod zewnętrzny nie mógł ich niewłaściwie używać, tworząc dodatkowe instancje.
W praktyce używanie singletona jest bardzo podobne do używania metod statycznych, z tym wyjątkiem, że można skorzystać z dziedziczenia i metod wirtualnych.
źródło
To jest możliwe!
Ale co dokładnie jest możliwe, zawęźmy. Ludzie często chcą jakiejś „statycznej funkcji wirtualnej” z powodu duplikowania kodu potrzebnego do wywołania tej samej funkcji poprzez statyczne wywołanie „SomeDerivedClass :: myfunction ()” i wywołanie polimorficzne „base_class_pointer-> myfunction ()”. „Legalną” metodą dopuszczenia takiej funkcjonalności jest powielanie definicji funkcji:
A co, jeśli klasa bazowa ma dużą liczbę funkcji statycznych, a klasa pochodna musi przesłonić każdą z nich, a jedna zapomniała podać powielającą się definicję funkcji wirtualnej. Dobrze, w czasie wykonywania pojawi się jakiś dziwny błąd który jest trudny do wyśledzenia. Bo powielanie kodu to zła rzecz. Poniższe próby próbują rozwiązać ten problem (i chcę wcześniej powiedzieć, że jest on całkowicie bezpieczny dla typów i nie zawiera żadnej czarnej magii, takiej jak typid czy dynamic_cast :)
Dlatego chcemy podać tylko jedną definicję metody getTypeInformation () na klasę pochodną i jest oczywiste, że musi to być definicja statycznafunkcja, ponieważ nie jest możliwe wywołanie „SomeDerivedClass :: getTypeInformation ()”, jeśli getTypeInformation () jest wirtualne. Jak możemy wywołać funkcję statyczną klasy pochodnej poprzez wskaźnik do klasy bazowej? W przypadku vtable nie jest to możliwe, ponieważ vtable przechowuje wskaźniki tylko do funkcji wirtualnych, a ponieważ zdecydowaliśmy się nie używać funkcji wirtualnych, nie możemy modyfikować vtable na naszą korzyść. Następnie, aby móc uzyskać dostęp do funkcji statycznej dla klasy pochodnej poprzez wskaźnik do klasy bazowej, musimy w jakiś sposób przechowywać typ obiektu w jego klasie bazowej. Jedną z metod jest stworzenie szablonu klasy bazowej za pomocą „ciekawie powtarzającego się wzorca szablonu”, ale nie jest to odpowiednie w tym przypadku i użyjemy techniki zwanej „wymazywaniem typów”:
Teraz możemy przechowywać typ obiektu w klasie bazowej „Object” ze zmienną „Keeper”:
W klasie pochodnej opiekun klasy musi zostać zainicjowany podczas konstruowania:
Dodajmy cukier syntaktyczny:
Teraz deklaracje potomków wyglądają następująco:
stosowanie:
Zalety:
Niedogodności:
Otwarte kwestie:
1) Istnieją różne nazwy funkcji statycznych i wirtualnych, jak rozwiązać tutaj niejednoznaczność?
2) jak niejawnie wywołać OVERRIDE_STATIC_FUNCTIONS wewnątrz każdego konstruktora?
źródło
Chociaż Alsk udzielił już dość szczegółowej odpowiedzi, chciałbym dodać alternatywę, ponieważ uważam, że jego ulepszona implementacja jest zbyt skomplikowana.
Zaczynamy od abstrakcyjnej klasy bazowej, która zapewnia interfejs dla wszystkich typów obiektów:
Teraz potrzebujemy rzeczywistej implementacji. Ale aby uniknąć konieczności pisania zarówno metod statycznych, jak i wirtualnych, będziemy mieć nasze rzeczywiste klasy obiektów dziedziczące metody wirtualne. Działa to oczywiście tylko wtedy, gdy klasa bazowa wie, jak uzyskać dostęp do statycznej funkcji składowej. Musimy więc użyć szablonu i przekazać mu rzeczywistą nazwę klasy obiektów:
Wreszcie musimy zaimplementować nasz prawdziwy obiekt (y). Tutaj musimy tylko zaimplementować statyczną funkcję składową, wirtualne funkcje składowe zostaną odziedziczone z klasy szablonu ObjectImpl, utworzona z nazwą klasy pochodnej, dzięki czemu uzyska dostęp do jej statycznych składowych.
Dodajmy kod do przetestowania:
Dodatek (12 stycznia 2019 r.):
Zamiast korzystać z funkcji GetClassNameStatic (), możesz również zdefiniować nazwę klasy jako statyczny element członkowski, nawet „inline”, który IIRC działa od C ++ 11 (nie przejmuj się wszystkimi modyfikatorami :)):
źródło
To jest możliwe. Twórz dwie funkcje: statyczną i wirtualną
źródło
Nie, nie jest to możliwe, ponieważ statyczne funkcje składowe nie mają
this
wskaźnika. A statyczne elementy składowe (zarówno funkcje, jak i zmienne) nie są tak naprawdę składnikami klas jako takimi. Tak się składa, że są przywoływane przezClassName::member
i stosują się do specyfikatorów dostępu do klas. Ich przechowywanie jest zdefiniowane gdzieś poza klasą; magazyn nie jest tworzony za każdym razem, gdy tworzysz instancję obiektu klasy. Wskaźniki do członków klas mają szczególne znaczenie w semantyce i składni. Wskaźnik do statycznego elementu członkowskiego jest normalnym wskaźnikiem pod każdym względem.funkcje wirtualne w klasie potrzebują
this
wskaźnika i są bardzo powiązane z klasą, dlatego nie mogą być statyczne.źródło
this
wskaźnika. funkcje statyczne nie są specyficzne dla instancji i nie będą ich potrzebować. Tak więc - to nie jest powód, dla którego wirtualne statyczne elementy członkowskie są niemożliwe.Cóż, dość późna odpowiedź, ale jest to możliwe przy użyciu ciekawie powtarzającego się wzorca szablonu. Ten artykuł na Wikipedii zawiera potrzebne informacje, a także przykład pod statycznym polimorfizmem jest tym, o co Cię proszą.
źródło
Myślę, że to, co próbujesz zrobić, można zrobić za pomocą szablonów. Próbuję czytać między wierszami. To, co próbujesz zrobić, to wywołanie metody z jakiegoś kodu, w którym wywołuje ona wersję pochodną, ale obiekt wywołujący nie określa, która klasa. Przykład:
Chcesz, aby Try () wywoływał wersję Bar M bez określania Bar. Sposób, w jaki robisz to w przypadku statyki, polega na użyciu szablonu. Więc zmień to tak:
źródło
Nie, statyczna funkcja członkowska nie może być wirtualna. Ponieważ koncepcja wirtualna jest rozwiązywana w czasie wykonywania za pomocą vptr, a vptr nie jest statycznym elementem klasy. Z powodu tej statycznej funkcji składowej nie można uzyskać dostępu do vptr, więc statyczny element członkowski może nie być wirtualnym.
źródło
Nie jest to możliwe, ale to tylko z powodu zaniedbania. To nie jest coś, co „nie ma sensu”, jak twierdzi wielu ludzi. Żeby było jasne, mówię o czymś takim:
Jest to w 100% coś, co można zaimplementować (po prostu nie ma) i argumentowałbym, że jest to przydatne.
Zastanów się, jak działają normalne funkcje wirtualne. Usuń
static
s i dodaj kilka innych rzeczy i mamy:Działa to dobrze iw zasadzie kompilator tworzy dwie tabele zwane tabelami VTables i przypisuje indeksy do takich funkcji wirtualnych
Następnie każda klasa z funkcjami wirtualnymi jest rozszerzana o inne pole wskazujące na jej VTable, więc kompilator zasadniczo zmienia je tak, aby wyglądały następująco:
Więc co się właściwie dzieje, kiedy dzwonisz
b->sayMyName()
? Zasadniczo to:(Pierwszy parametr zmieni się na
this
.)Dobrze, więc jak by to działało ze statycznymi funkcjami wirtualnymi? Jaka jest różnica między statycznymi i niestatycznymi funkcjami składowymi? Jedyną różnicą jest to, że te ostatnie otrzymują
this
wskaźnik.Dokładnie to samo możemy zrobić ze statycznymi funkcjami wirtualnymi - wystarczy usunąć
this
wskaźnik.To mogłoby wtedy obsługiwać obie składnie:
Więc zignoruj wszystkich przeciwników. To ma sens. Dlaczego więc nie jest obsługiwany? Myślę, że to dlatego, że ma bardzo małe korzyści, a nawet może być trochę zagmatwane.
Jedyną przewagą techniczną w porównaniu ze zwykłą funkcją wirtualną jest to, że nie trzeba przechodzić
this
do funkcji, ale nie sądzę, aby miało to jakikolwiek wymierny wpływ na wydajność.Oznacza to, że nie masz oddzielnej funkcji statycznej i niestatycznej w przypadkach, gdy masz instancję i nie masz instancji, ale może być również mylące, że jest ona naprawdę „wirtualna” tylko wtedy, gdy używasz wywołanie instancji.
źródło
Nie, nie jest to możliwe, ponieważ statyczne elementy członkowskie są powiązane w czasie kompilacji, podczas gdy wirtualne elementy członkowskie są powiązane w czasie wykonywania.
źródło
Po pierwsze, odpowiedzi są prawidłowe, że to, czego żąda OP, jest sprzecznością pod względem: metody wirtualne zależą od typu instancji w czasie wykonywania; Funkcje statyczne w szczególności nie zależą od instancji - tylko od typu. To powiedziawszy, sensowne jest, aby funkcje statyczne zwracały coś specyficznego dla typu. Na przykład, miałem rodzinę klas MouseTool dla wzorca State i zacząłem mieć każdą z nich funkcję statyczną zwracającą modyfikator klawiatury, który był z nią powiązany; Użyłem tych statycznych funkcji w funkcji fabrycznej, która utworzyła poprawną instancję MouseTool. Ta funkcja sprawdziła stan myszy względem MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier () itp., A następnie utworzyła odpowiedni. Oczywiście później chciałem sprawdzić, czy stan jest właściwy, więc chciałem napisać coś w rodzaju „
Jeśli więc chcesz tego, możesz zmienić swoje rozwiązanie. Mimo to rozumiem pragnienie posiadania statycznych metod, a następnie wywołuję je dynamicznie w oparciu o dynamiczny typ instancji. Myślę, że wzorzec gości może dać ci to, czego chcesz. Daje ci to, czego chcesz. To trochę dodatkowego kodu, ale może być przydatny dla innych odwiedzających.
Więcej informacji można znaleźć pod adresem : http://en.wikipedia.org/wiki/Visitor_pattern .
Następnie dla każdego konkretnego obiektu:
a następnie zdefiniuj gościa podstawowego:
Następnie konkretny gość, który wybiera odpowiednią funkcję statyczną:
na koniec użyj go:
Uwagi:
Jeśli chcesz uniknąć błędów kopiuj-wklej, w przypadku których jedna z metod odwiedzania wywołuje niewłaściwą funkcję statyczną, możesz użyć funkcji pomocniczej opartej na szablonie (która sama w sobie nie może być wirtualna) t gościa z szablonem takim jak ten:
źródło
Foo foo; ... foo::bar();
ZamiastFoo::bar();
). Nie jest inaczej,decltype(foo)::bar();
ale to znowu byłoby statycznie związane. Podejście gościa wydaje się rozsądnym sposobem uzyskania tego zachowania bez robienia statycznej metody jako wirtualnej metody stałej.W języku c ++ możesz używać dziedziczenia statycznego z metodą crt. Na przykład jest szeroko stosowany w szablonie okna atl & wtl.
Zobacz https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Mówiąc prościej, masz klasę, która jest tworzona z samego siebie, podobnie jak klasa myclass: public myancestor. Od tego momentu klasa myancestor może teraz wywołać twoją statyczną funkcję T :: YourImpl.
źródło
Może możesz wypróbować moje rozwiązanie poniżej:
źródło
Base::mSelf
odnosi się do OSTATNIO skonstruowanej instancji dowolnej klasy pochodnej, nawet jeśli ta instancja została zniszczona . więcclass D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */
co NIE jest tym, co jest potrzebne.Jak powiedzieli inni, istnieją 2 ważne informacje:
this
wskaźnika podczas wykonywania statycznego wywołania funkcji ithis
punkty wskaźnik do struktury, gdzie tabela wirtualny lub thunk, są wykorzystywane do wyszukiwania, która metoda Runtime zadzwonić.Funkcja statyczna jest określana w czasie kompilacji.
Pokazałem ten przykład kodu w statycznych elementach członkowskich C ++ w klasie ; pokazuje, że można wywołać metodę statyczną, mając wskaźnik zerowy:
źródło
p < null
,p >= null
Wszystkie są również niezdefiniowane.