Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

06.09.2006, 08:18

Wofür virtuelle Funktionen?

Was hat es denn nun genau mit diesem dubiosen "virtual" auf sich?

Wenn ich in einer Klasse eine Funktion als virtual kennzeichne, macht sie genau das gleiche wie wenn ich es nicht tue. Auch wenn ich eine andere Klasse von dieser ableite, dann tut die Funktion immer noch das gleiche. Und wenn ich diese Funktion in der abgeleiteten Klasse überschreibe dann nimmt er logischer Weise die neue Funktion. Aber da ist es vollkommen egal ob die Funktion in der Basisklasse virtual ist oder nicht.

Laut meinen Recherchen und meinen Büchern kennzeichnet man eine Funktion die von abgeleiteten Klassen verändert werden soll mit virtual. Aber wenn man es nicht macht gehts doch auch. Wieso also das ganze?

Phil_GDM

Alter Hase

Beiträge: 443

Wohnort: Graz

Beruf: Student-Softwareentwicklung u. Wissensmanagement

  • Private Nachricht senden

2

06.09.2006, 10:31

Erstmal: virtual bringt nur in Zusammenhang mit Pointern oder Referenzen etwas.

Nehmen wir mal folgenden Code

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

using namespace std;

class Base
{
public:
  virtual void fun()
  {
    cout << "Base" << endl;
  }
};

class Derived : public Base
{
public:
  void fun()
  {
    cout << "Derived" << endl;
  }
};


void foo(Base& d)
{
  d.fun();
}

int main()
{
  Derived d;
  foo(d);
  getchar();
}


Wenn du diesen ausführst, wird Derived ausgegeben.
Wenn du nun das Virtual wegnimmst, wird Base ausgegeben. Der Grund dafür ist, dass der Typ, der in foo verwendet wird Base& ist, denn ohne das virtual wird die Implementierung vom Übergabetyp, also Base genommen. Wenn du die Methode nun aber als virtual deklarierst, dann sagst du, nimm nicht die Implementierung des vorhandenen Datentyps, sondern schau in der VTabelle nach, welche Methode genommen werden muss.
Da das übergebene Objekt als Derived erzeugt wurde, ist in der VTabelle die Implementierung von Derived eingetragen.

Wenn du in der Funktion foo den Typ von Base& auf Base änderst, wird immer Base ausgegeben, egal ob du die Funktion als virtual deklarierst oder nicht, wo wir wieder bei meinem ersten Satz angelangt wären :).

HTH

mfg Philipp

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

3

06.09.2006, 12:25

Noch ein anderes Beispiel:

Man möchte eine Liste von Figuren haben, die sich alle selbst zeichnen können. Es gibt Linien, Kreise, Rechtecke, die alle von der Klasse Figure abgeleitet werden. In der Liste kann ich mir nur Zeiger auf meine Basisklasse Figur merken, was aber kein Problem ist, weil ich einen Zeiger einer abgeleiteten Klasse auch einem Zeiger der Basisklasse zuweisen kann. (Umgekehrt ist das nicht möglich!)

Darum hat die Klasse Figur eine virtuelle Methode "zeichnen" die ich dann in den konkreten Implementationen von Linie, Kreis und Rechteck implementiere.

Wenn ich jetzt meine Liste von Figuren durchlaufe und für jeden Figur-Zeiger die "zeichnen" Methode aufrufe, wird je nach konkretem Objekt eine Linie, ein Kreis oder ein Rechteck gezeichnet.

Ohne virtuelle "zeichnen" Methode würde immer nur "Figur::zeichnen" aufgerufen. Es macht praktisch sogar gar keinen Sinn, ein Figur Objekt in der Liste zu speichern, weil man sowas allgemeines nicht zeichnen kann.

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Black-Panther

Alter Hase

Beiträge: 1 443

Wohnort: Innsbruck

  • Private Nachricht senden

4

06.09.2006, 12:27

Außerdem gibts über virtual auch noch die puren virtuellen Methoden... Sie sind praktisch eine REINE Vorlage. D.h für abstrakte Klassen. Wenn du zB ein Interface programmierst, dass der User verwenden soll, und von einer bestimmten Klasse gewisse Methoden erwartest, weil jene von deinem System aufgerufen werden, bist du hier an der richtigen Adresse!

zB:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class IBase
{
private:
    bool    m_bActive;

public:
    virtual bool    Init() = 0; //Pure virtuelle Methode

    virtual bool    Exit() = 0; //...

};

//Abgeleitete Klasse vom User. Die puren virtuellen Methoden MÜSSEN implementiert werden, sonst Compilerfehler!

class CDerived : public IBase
{
public:
    bool Init();
    bool Exit();
};

//Und in deinem System kannst du dann ohne die Derived Klasse zu 

//implementieren die funktionen Init und Exit ohne Probleme verweden

//weil du sie ja als pure virtuelle deklariert hast und das als interface verwendest
stillalive studios
Drone Swarm (32.000 Dronen gleichzeitig steuern!)
facebook, twitter

5

06.09.2006, 14:05

Ah ja, langsam lichtet sich die virtuelle Dunkelheit ;-)

Also wird bei virtuellen Methoden die tatsächlich übergebene Klasse (z.B. abgeleitete Klasse welche ja in ihre Basisklasse umgewandelt werden kann) benutzt und bei nicht-virtuellen die als Übergabe erwartete (also der Typ des Übergabeparameters) Klasse ?

Kann man dann also Grundsätzlich sagen, das wenn vererbte Methoden angepasst werden sollen (von den beerbten Klassen), man sie IMMER als virtual deklarieren sollte? Oder gibts auch Situationen in der das nicht sinnvoll ist?

Und was hat es nun mit den virtuellen Destruktoren auf sich? Würde dann evtl. der falsche Destruktor aufgerufen (von der Basisklasse anstelle der abgeleiteten Klasse?)

Black-Panther

Alter Hase

Beiträge: 1 443

Wohnort: Innsbruck

  • Private Nachricht senden

6

06.09.2006, 14:28

Man kann nicht generell sagen, wie mans machn soll... kommt drauf an, was du damit erreichen willst. Das musst du dann zu jenem Zeitpunkt entscheiden.
Für Destruktoren gilt das selbe wie für Methoden... Wenn du ihn als virtual deklarierst, wird zuerst der Destruktor der klasse CDerived und dann der von CBase aufgerufen... sonst nur der von CBase...
stillalive studios
Drone Swarm (32.000 Dronen gleichzeitig steuern!)
facebook, twitter

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

7

07.09.2006, 12:10

Ganz wichtig:

Eine Klasse mit virtuellen Methoden muss immer einen virtuellen Destruktor haben, denn sonst würde das folgende Konstrukt immer etwas Falsches machen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
class Base;
class Derived : public Base;

void foo ()
{
  Base* ptr = new Derived ();
  delete ptr;
}


Wenn das Derived-Objekt Speicher allociert oder Resources reserviert, die im Destruktor wieder freigegeben werden, und der Destruktor nicht virtuell wäre, würde im delete nur der Destruktor von Base aufgerufen das nichts von den Resourcen weiss --> Speicherleck oder Schlimmeres. :evil:

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Werbeanzeige