Odpowiedzi:
Z funkcji wirtualnej Wikipedii ...
W programowaniu obiektowym, w językach takich jak C ++ i Object Pascal, funkcja wirtualna lub metoda wirtualna to dziedziczna i dająca się zastąpić funkcja lub metoda, dla której ułatwione jest dynamiczne wysyłanie. Ta koncepcja jest ważną częścią polimorfizmu (środowiska wykonawczego) programowania obiektowego (OOP). Krótko mówiąc, funkcja wirtualna określa funkcję docelową do wykonania, ale cel może nie być znany w czasie kompilacji.
W przeciwieństwie do funkcji niebędącej funkcją wirtualną, gdy funkcja wirtualna jest nadpisywana, najbardziej pochodna wersja jest używana na wszystkich poziomach hierarchii klas, a nie tylko na poziomie, na którym została utworzona. Dlatego jeśli jedna metoda klasy bazowej wywołuje metodę wirtualną, zamiast wersji zdefiniowanej w klasie bazowej zostanie użyta wersja zdefiniowana w klasie pochodnej.
Jest to sprzeczne z funkcjami innymi niż wirtualne, które nadal można przesłonić w klasie pochodnej, ale „nowa” wersja będzie używana tylko przez klasę pochodną i poniżej, ale w ogóle nie zmieni funkcjonalności klasy podstawowej.
natomiast..
Funkcja czysto wirtualna lub metoda czysto wirtualna to funkcja wirtualna, która musi zostać zaimplementowana przez klasę pochodną, jeśli klasa pochodna nie jest abstrakcyjna.
Gdy istnieje metoda czysto wirtualna, klasa jest „abstrakcyjna” i nie można jej tworzyć samodzielnie. Zamiast tego należy użyć klasy pochodnej, która implementuje metody czysto wirtualne. Czysta-wirtualna nie jest w ogóle zdefiniowana w klasie podstawowej, więc klasa pochodna musi ją zdefiniować, lub ta klasa pochodna jest również abstrakcyjna i nie można jej utworzyć. Instancja może być utworzona tylko klasa, która nie ma metod abstrakcyjnych.
Wirtualny zapewnia sposób na zastąpienie funkcjonalności klasy podstawowej, a czysty wirtualny tego wymaga .
pure
słowo kluczowe, ale Bell Labs zamierzał wydać główną wersję C ++, a jego menedżer nie pozwoliłby na to na późnym etapie. Dodawanie słów kluczowych to wielka sprawa.
Chciałbym skomentować definicję wirtualnego Wikipedii, którą kilka osób tutaj powtórzyło. [W momencie pisania tej odpowiedzi] Wikipedia zdefiniowała metodę wirtualną jako metodę, którą można zastąpić w podklasach. [Na szczęście Wikipedia była od tamtej pory edytowana i teraz wyjaśnia to poprawnie.] To jest niepoprawne: każdą metodę, nie tylko wirtualną, można zastąpić w podklasach. Wirtualny daje polimorfizm, czyli możliwość wybrania w czasie wykonywania najbardziej pochodnego zastąpienia metody .
Rozważ następujący kod:
#include <iostream>
using namespace std;
class Base {
public:
void NonVirtual() {
cout << "Base NonVirtual called.\n";
}
virtual void Virtual() {
cout << "Base Virtual called.\n";
}
};
class Derived : public Base {
public:
void NonVirtual() {
cout << "Derived NonVirtual called.\n";
}
void Virtual() {
cout << "Derived Virtual called.\n";
}
};
int main() {
Base* bBase = new Base();
Base* bDerived = new Derived();
bBase->NonVirtual();
bBase->Virtual();
bDerived->NonVirtual();
bDerived->Virtual();
}
Jaka jest wydajność tego programu?
Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.
Pochodne zastępuje każdą metodę Base: nie tylko wirtualną, ale także niewirtualną.
Widzimy, że gdy masz wskaźnik bazowy na pochodną (bDerived), wywołanie NonVirtual wywołuje implementację klasy Base. Jest to rozwiązywane w czasie kompilacji: kompilator widzi, że bDerived jest Bazą *, że NonVirtual nie jest wirtualny, więc robi to z klasą Base.
Jednak wywołanie Virtual wywołuje implementację klasy Derived. Z powodu słowa kluczowego virtual wybór metody odbywa się w czasie wykonywania , a nie w czasie kompilacji. To, co dzieje się tutaj podczas kompilacji, polega na tym, że kompilator widzi, że jest to Base * i że wywołuje metodę wirtualną, więc wstawia wywołanie do vtable zamiast do klasy Base. Ta tabela vt jest tworzona w czasie wykonywania, stąd rozdzielczość w czasie wykonywania do najbardziej pochodnego przesłonięcia.
Mam nadzieję, że nie było to zbyt mylące. Krótko mówiąc, każdą metodę można zastąpić, ale tylko metody wirtualne dają polimorfizm, to znaczy wybór najbardziej pochodnej zmiany w czasie wykonywania. W praktyce jednak zastąpienie metody innej niż wirtualna jest uważane za złą praktykę i rzadko stosowane, dlatego wiele osób (w tym ktokolwiek napisał ten artykuł w Wikipedii) uważa, że można zastąpić tylko metody wirtualne.
Derived*
wywołań z tymi samymi funkcjami, aby doprowadzić punkt do domu. W przeciwnym razie świetna odpowiedź
Wirtualne słowo kluczowe daje C ++ jego zdolność do wspierania polimorfizmu. Gdy masz wskaźnik do obiektu jakiejś klasy, takiego jak:
class Animal
{
public:
virtual int GetNumberOfLegs() = 0;
};
class Duck : public Animal
{
public:
int GetNumberOfLegs() { return 2; }
};
class Horse : public Animal
{
public:
int GetNumberOfLegs() { return 4; }
};
void SomeFunction(Animal * pAnimal)
{
cout << pAnimal->GetNumberOfLegs();
}
W tym (głupim) przykładzie funkcja GetNumberOfLegs () zwraca odpowiednią liczbę na podstawie klasy obiektu, do którego została wywołana.
Teraz rozważ funkcję „SomeFunction”. Nie ma znaczenia, jaki typ obiektu zwierzęcego jest do niego przekazywany, o ile pochodzi on od Zwierząt. Kompilator automatycznie zarzuci dowolną klasę pochodzenia zwierzęcego na zwierzę, ponieważ jest to klasa bazowa.
Jeśli to zrobimy:
Duck d;
SomeFunction(&d);
wyświetli „2”. Jeśli to zrobimy:
Horse h;
SomeFunction(&h);
wyświetli „4”. Nie możemy tego zrobić:
Animal a;
SomeFunction(&a);
ponieważ nie można go skompilować, ponieważ funkcja wirtualna GetNumberOfLegs () jest czysta, co oznacza, że musi zostać zaimplementowana przez wyprowadzenie klas (podklas).
Czyste funkcje wirtualne są najczęściej używane do definiowania:
a) klasy abstrakcyjne
Są to klasy podstawowe, z których należy wywodzić, a następnie implementować funkcje czysto wirtualne.
b) interfejsy
Są to „puste” klasy, w których wszystkie funkcje są czysto wirtualne i dlatego musisz wyprowadzić, a następnie zaimplementować wszystkie funkcje.
W klasie C ++ słowo wirtualne jest słowem kluczowym, które to oznacza, metoda może zostać zastąpiona (tj. Zaimplementowana) przez podklasę. Na przykład:
class Shape
{
public:
Shape();
virtual ~Shape();
std::string getName() // not overridable
{
return m_name;
}
void setName( const std::string& name ) // not overridable
{
m_name = name;
}
protected:
virtual void initShape() // overridable
{
setName("Generic Shape");
}
private:
std::string m_name;
};
W takim przypadku podklasa może zastąpić funkcję initShape w celu wykonania specjalistycznej pracy:
class Square : public Shape
{
public:
Square();
virtual ~Square();
protected:
virtual void initShape() // override the Shape::initShape function
{
setName("Square");
}
}
Termin czysto wirtualny odnosi się do funkcji wirtualnych, które muszą zostać zaimplementowane przez podklasę i nie zostały zaimplementowane przez klasę podstawową. Metodę wyznacza się jako czystą wirtualną, używając słowa kluczowego virtual i dodając a = 0 na końcu deklaracji metody.
Więc jeśli chcesz uczynić Shape :: initShape czystym wirtualnym, wykonaj następujące czynności:
class Shape
{
...
virtual void initShape() = 0; // pure virtual method
...
};
Dodając do klasy czystą wirtualną metodę, uczynisz klasę abstrakcyjną klasą podstawową, co jest bardzo przydatne do oddzielania interfejsów od implementacji.
m_name
. Co m_
znaczy
„Wirtualny” oznacza, że metoda może być nadpisana w podklasach, ale ma bezpośrednio wywoływalną implementację w klasie podstawowej. „Czysta wirtualna” oznacza, że jest to metoda wirtualna bez implementacji, którą można wywołać bezpośrednio. Taki sposób musi zostać co najmniej raz zastąpiony w hierarchii dziedziczenia - jeśli klasa ma jakieś niezaimplementowane metody wirtualne, obiektów tej klasy nie można zbudować i kompilacja się nie powiedzie.
@quark wskazuje, że metody czysto wirtualne mogą mieć implementację, ale ponieważ metody czysto wirtualne muszą zostać zastąpione, domyślnej implementacji nie można bezpośrednio wywołać. Oto przykład metody czysto wirtualnej z domyślną:
#include <cstdio>
class A {
public:
virtual void Hello() = 0;
};
void A::Hello() {
printf("A::Hello\n");
}
class B : public A {
public:
void Hello() {
printf("B::Hello\n");
A::Hello();
}
};
int main() {
/* Prints:
B::Hello
A::Hello
*/
B b;
b.Hello();
return 0;
}
Według komentarzy to, czy kompilacja się nie powiedzie, zależy od kompilatora. Przynajmniej w GCC 4.3.3 nie kompiluje się:
class A {
public:
virtual void Hello() = 0;
};
int main()
{
A a;
return 0;
}
Wynik:
$ g++ -c virt.cpp
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note: because the following virtual functions are pure within ‘A’:
virt.cpp:3: note: virtual void A::Hello()
Jak działa wirtualne słowo kluczowe?
Załóżmy, że człowiek jest klasą podstawową, a Indian pochodzi od człowieka.
Class Man
{
public:
virtual void do_work()
{}
}
Class Indian : public Man
{
public:
void do_work()
{}
}
Zadeklarowanie do_work () jako wirtualnego oznacza po prostu: które do_work () do wywołania zostaną określone TYLKO w czasie wykonywania.
Załóżmy, że tak
Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.
Jeśli wirtualny nie jest używany, to samo jest określane statycznie lub statycznie powiązane przez kompilator, w zależności od tego, który obiekt wywołuje. Więc jeśli obiekt Man wywołuje do_work (), to man do_work () jest nazywany NAWET ZA POMOCĄ PUNKTÓW DO INDYJSKIEGO OBIEKTU
Uważam, że najczęściej głosowana odpowiedź jest myląca - Każda metoda, niezależnie od tego, czy wirtualna może mieć nadpisaną implementację w klasie pochodnej. W konkretnym odniesieniu do C ++ poprawną różnicą jest powiązanie w czasie wykonywania (gdy używany jest wirtualny) i czas kompilacji (gdy nie jest używany wirtualny, ale metoda jest nadpisana, a wskaźnik bazowy jest wskazywany na obiekcie pochodnym) powiązanie powiązanych funkcji.
Wydaje się, że istnieje inny mylący komentarz, który mówi:
„Justin,„ czysta wirtualna ”to tylko termin (nie słowo kluczowe, patrz moja odpowiedź poniżej) oznaczający„ ta funkcja nie może zostać zaimplementowana przez klasę podstawową ”.
TO JEST ŹLE! Funkcje czysto wirtualne mogą również mieć ciało I MOŻNA BYĆ WDROŻONE! Prawda jest taka, że czystą funkcję wirtualną klasy abstrakcyjnej można nazwać statycznie! Dwaj bardzo dobrzy autorzy to Bjarne Stroustrup i Stan Lippman .... ponieważ napisali ten język.
Funkcja wirtualna jest funkcją składową zadeklarowaną w klasie bazowej i przedefiniowaną przez klasę pochodną. Funkcje wirtualne są hierarchiczne w kolejności dziedziczenia. Gdy klasa pochodna nie przesłania funkcji wirtualnej, używana jest funkcja zdefiniowana w jej klasie bazowej.
Czysta funkcja wirtualna to taka, która nie zawiera definicji względem klasy podstawowej. Nie ma implementacji w klasie bazowej. Każda klasa pochodna musi zastąpić tę funkcję.
Simula, C ++ i C #, które domyślnie używają statycznego wiązania metod, programista może określić, że określone metody powinny używać wiązania dynamicznego, oznaczając je jako wirtualne. Dynamiczne wiązanie metod ma kluczowe znaczenie dla programowania obiektowego.
Programowanie obiektowe wymaga trzech podstawowych pojęć: enkapsulacji, dziedziczenia i dynamicznego wiązania metod.
Hermetyzacja pozwala ukryć szczegóły implementacji abstrakcji za prostym interfejsem.
Dziedziczenie pozwala zdefiniować nową abstrakcję jako rozszerzenie lub udoskonalenie istniejącej abstrakcji, uzyskując automatycznie niektóre lub wszystkie jej cechy.
Dynamiczne wiązanie metod pozwala nowej abstrakcji wyświetlać nowe zachowanie, nawet gdy jest używana w kontekście, który oczekuje starej abstrakcji.
Metody wirtualne MOGĄ zostać zastąpione przez wyprowadzenie klas, ale wymagają implementacji w klasie bazowej (tej, która zostanie zastąpiona)
Metody czysto wirtualne nie mają implementacji klasy podstawowej. Muszą być zdefiniowane przez klasy pochodne. (Więc technicznie zastąpienie nie jest właściwym terminem, ponieważ nie ma nic do zastąpienia).
Wirtualny odpowiada domyślnemu zachowaniu Java, gdy klasa pochodna zastępuje metodę klasy podstawowej.
Metody Pure Virtual odpowiadają zachowaniu metod abstrakcyjnych w klasach abstrakcyjnych. A klasa, która zawiera wyłącznie metody wirtualne i stałe, byłaby cpp-wisiorek interfejsu.
Czysta funkcja wirtualna
wypróbuj ten kod
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()=0;
};
class anotherClass:aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"hellow World";
}
};
int main()
{
//aClassWithPureVirtualFunction virtualObject;
/*
This not possible to create object of a class that contain pure virtual function
*/
anotherClass object;
object.sayHellow();
}
W klasie anotherClass usuń funkcję sayHellow i uruchom kod. dostaniesz błąd! Ponieważ gdy klasa zawiera czystą funkcję wirtualną, z tej klasy nie można utworzyć żadnego obiektu i jest ona dziedziczona, to klasa pochodna musi zaimplementować tę funkcję.
Funkcja wirtualna
wypróbuj inny kod
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()
{
cout<<"from base\n";
}
};
class anotherClass:public aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"from derived \n";
}
};
int main()
{
aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
baseObject->sayHellow();///call base one
baseObject=new anotherClass;
baseObject->sayHellow();////call the derived one!
}
W tym przypadku funkcja sayHellow jest oznaczona jako wirtualna w klasie bazowej. Mówi kompilator, który próbuje wyszukać funkcję w klasie pochodnej i implementuje funkcję. Jeśli nie zostanie znaleziony, uruchom funkcję podstawową.
„Funkcja wirtualna lub metoda wirtualna to funkcja lub metoda, której zachowanie można zastąpić w klasie dziedziczącej funkcją o tej samej sygnaturze” - Wikipedia
To nie jest dobre wytłumaczenie funkcji wirtualnych. Ponieważ nawet jeśli członek nie jest wirtualny, dziedziczenie klas może go zastąpić. Możesz spróbować to zobaczyć sam.
Różnica pojawia się, gdy funkcja przyjmuje klasę podstawową jako parametr. Gdy podajesz klasę dziedziczącą jako dane wejściowe, funkcja ta wykorzystuje implementację klasy podstawowej funkcji przesłonięcia. Jeśli jednak ta funkcja jest wirtualna, używa tej, która jest zaimplementowana w klasie pochodnej.
Funkcje wirtualne muszą mieć definicję w klasie bazowej, a także w klasie pochodnej, ale nie są konieczne, na przykład funkcja ToString () lub toString () jest funkcją wirtualną, dzięki czemu można zapewnić własną implementację, zastępując ją w klasach zdefiniowanych przez użytkownika.
Funkcje wirtualne są deklarowane i definiowane w normalnej klasie.
Czysta funkcja wirtualna musi być zadeklarowana z końcem „= 0” i może być zadeklarowana tylko w klasie abstrakcyjnej.
Klasa abstrakcyjna posiadająca wyłącznie funkcje wirtualne nie może mieć definicji tych funkcji czysto wirtualnych, więc oznacza to, że implementacja musi być zapewniona w klasach pochodzących z tej klasy abstrakcyjnej.