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

13.01.2007, 17:59

Schlüsselwort: virtual

Hallo mal wieder und wieder ne frage, die ich mir heut gestellt hab :)

also mit virtual kann man bei klassen sagen, dass eben die funktion "virtuell" ist und von anderen unterklassen, die von dieser klasse abhängig sind, verwendet werden, aber theoretisch dort neu definiert sind und somit anders aussehen und das programm dann DIESE funktion aufruft und nicht die virtual.


so jetzt hatte ich folgendes "problem":

ne funktion ShowStats, die einfach die werte der variablen in der klasse ausgibt:
z.b. so

C-/C++-Quelltext

1
2
3
4
5
void CClass ShowStats()
{
 cout << iNumber << "\n"
       << cName << "\n\n";
}


so die funktion hat bei allen unterklasse funktioniert, außer bei einer, nämlich bei einer unterklasse, die noch ne zusätzliche variable hatte.

z.b. wie im buch der minenleger, der von CRaumschiff abhängt, aber eben noch die variable AnzahlMinen hatte. bei mir wars eben ne variable iSuperpower und genau mit dieser klasse funktionierte die showstats funktion nicht. wohlgemerkt,als virtual noch nicht davor stand.

als die funktion dann als virtual void ShowStats(); vordefiniert war gings dann einwandfrei.

meine frage: Warum? oO
irgendwie ergab das für mich 0 sinn, da mit der zusatzvariable absolut nichts passierte, die wurde nicht mal mit cout ausgegeben nichts... sehr merkwürdig :)

grek40

Alter Hase

Beiträge: 1 491

Wohnort: Dresden

  • Private Nachricht senden

2

13.01.2007, 18:40

könntest du das mal ganz praktisch mit den entsprechenden Klassendefinitionen unterlegen? mir ist grad noch nich klar, was du meinst.

3

13.01.2007, 19:33

achso k also hier

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
35
36
37
// Enemy.hpp

// author: Simon Klausner

// version: 0.1

// date: 10.1.07

// purpose: define class CEnemy and subclasses


class CHero;


// Basic class

class CEnemy
{
protected:
    // Variables

    int iHitpoints, iHitpointsMAX, iSpellpoints;    // Hitpoints and Spellpoints

    double dArmor;                                                              // Armor

    int iDamage, iDamageMAX;                                            // Damage

    int iLevel, iExperience;                                            // Level, Experience given

    char *cName;                                                                    // Name


public:
    // Functions

                // ...

    virtual void ShowStats();                                   // Shows Stats

};
// ...

class CDistos : public CEnemy
{
private:
    // Variables

    int iSuperpower;

public:
    // Functions

    CDistos();
    void Superpower(CHero *pHero, CEnemy *pEnemy[7]);       // Superpower

};


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Show Stats

void CEnemy::ShowStats()
{
    // Variables

    char cEnter = 0;

    cout << "\n\n\n" << cName << " (" << iLevel << "):\n"
             << "Erfahrung: " << iExperience << "\n\n";
    printf("Ruestung: %.2f\n", dArmor);
    cout << "Schaden: " << iDamage << " - " << iDamageMAX << "\n"
             << "Leben: " << iHitpoints << " von " << iHitpointsMAX << "\n\n\n";
    cout << "\nWeiter mit Enter...\n";
    do {cEnter = getchar();} while(cEnter != '\n');
}   // end Show Stats


wenn man da eben bei showstats nicht virtual davor schreibt und z.b. folgendes in ner funktion verwendet.

C-/C++-Quelltext

1
2
3
//...

pEnemy = new CDistos;
pEnemy->ShowStats();


da kommt dann ein fehler,wenn das virtual eben nicht davor steht :)

grek40

Alter Hase

Beiträge: 1 491

Wohnort: Dresden

  • Private Nachricht senden

4

13.01.2007, 19:42

Ein Laufzeitfehler, ein Kompilierfehler oder ein logischer Fehler?
Gib ma genauen Fehlertext dazu... weil nach meinem Verständnis müsste zwar ohne virtual die Funktion der Basisklasse aufgerufen werden, aber funktionieren müsste es.

5

13.01.2007, 20:13

lol frag mich ned,aber jetzt gehts auf einmal auch ohne virtual oO

lag wohl weiter oben im code, wo ich bei der gegnerkreierung mit dem switch ein break; vergessen habe, anders kann ichs mir ned erklären lol^^

so jetzt kommt ne frage noch in den release thread mom :D

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

6

14.01.2007, 12:33

Mooment, du hast das Schlüsselwort virtual anscheinend nicht ganz verstanden. Virtuelle Funktionen können von abgeleiteten Klassen überschrieben werden. Das ist grundsätzlich notwendig wenn man mit Basiszeigern auf abgeleitete Klassen arbeitet.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Base
{
public:
    // ...


    void foobar() const
    {
        std::cout << "void Base::foobar() const" << std::endl;
    }
};

class Derived
    : public Base
{
public:
    void foobar() const
    {
        std::cout << "void Derived::foobar() const" << std::endl;
    }
};


Folgender Code läuft einwandfrei:

C-/C++-Quelltext

1
2
3
4
5
Base base;
Derived derived;

base.foobar();
derived.foobar();


Auch folgender Code liefert keine Probleme:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
Base* base = new Base;
Derived* derived = new Derived;

base->foobar();
derived->foobar();

delete base;
delete derived;


Aber folgendes klappt eben nicht:

C-/C++-Quelltext

1
2
3
4
5
Base* base = new Derived;

base->foobar();

delete base;


Hier ist die Ausgabe: void Base::foobar() const. Eigentlich sollte die Ausgabe ja aber die der Klasse Derived sein, da base ja auf ein Derived Objekt zeigt.
Die Lösung bietet das Schlüsselwort virtual.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
class Base
{
public:
    // ...


    virtual void foobar() const
    {
        std::cout << "void Base::foobar() const" << std::endl;
    }
};


Und schon läuft der Code wie gewollt. D.h. also: Alle Funktionen die einmal überschrieben werden sollen sind als virtuell zu deklarieren!
Außerdem machen viele, gerade Anfänger, einen großen Fehler. Sie machen zwar alle Funktionen schön virtuell, aber vergessen den Destruktor auch virtuell zu machen.
Das Problem ist das ein nicht virtueller Destruktor logischerweise zu Problemen beim zerstören von Objekten führen kann, was schnell zu Speicherlöchern führt.

Also merke: Alle Klassen die als Basisklase funktionieren sollen, müssen einen virtuellen Destruktor haben. Ist dies nicht der Fall kann das zu undefiniertem Verhalten beim Objektzerstören kommen.

Also in unserem Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
class Base
{
public:
    // ...

    virtual ~Base()
    {}

    virtual void foobar() const
    {
        std::cout << "void Base::foobar() const" << std::endl;
    }
};


grüße
@D13_Dreinig

odc

Frischling

  • Private Nachricht senden

7

05.02.2007, 13:39

Hallo community,

sorry - aber diese ganze virtual-Geschichte habe ich immer noch nicht gefressen. Ich habe folgende Miniklasse mal aufgemalt:

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
35
#include <iostream>

using namespace std;

//classes

//

class CHorse
{
public:
    virtual void move()
    {
        cout << "I can move!" << endl;
    }
};

class Pegasus : public CHorse
{
public:
    void move()
    {
        cout << "I can FLY!" << endl;
    }
};

int main()
{
    //variables

    CHorse newCHorse;
    newCHorse.move();

    Pegasus Pegasus;
    Pegasus.move();

    return 0;
}


Also, ganz egal, ob die Basisklassen-funktion move() als virtuell oder nicht deklariert wird - das Beispiel wird immer kompiliert. Und ich dachte, man muss IMMER, wenn eine Funktion von einer abgeleiteten Klasse benutzt und überschrieben wird, eben diese Funktion als virtual deklarieren.[/cpp]
code is poetry

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

8

05.02.2007, 13:49

Klar klappt das. Es geht um folgenden Fall:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
CHorse* ptr1 = new CHorse;
CHorse* ptr2 = new Pegasus;

ptr1->move(); // ---> Aufruf von CHorse::move()

ptr2->move(); // ---> Aufruf von Pegasus::move()


delete ptr1; // Zerstören des CHorse Objekts

delete ptr2; // Zerstören des CHorseteils UND Pegasusteils des Pegasusobjekts


Aus diesem Grund gibt es das Schlüsselwort virtual, sollte das nämlich nicht vorhanden sein verhält sich dein Programm nicht wie gewünscht. Kannst es ja ausprobieren! ;)

Achja: Basisklassen müssen immer einen virtuellen Destruktor haben!

grüße
@D13_Dreinig

odc

Frischling

  • Private Nachricht senden

9

05.02.2007, 14:31

Danke für Deine Antwort - gleich hinterher die nächste doofe Frage(n):

1. Was ich gecodet habe, übergibt ja ganze Objekte, also riesige Werte. Was Du dagegen gemacht hast, war ja einfach "nur" Zeiger zu instanziieren. Ist das wirtschaftlicher und "schöner" und einfach besserer Code?

2. Die virtual-Funktion wird immer nur dann benutzt, wenn ich obige Zeiger verwende. Richtig?
code is poetry

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

10

05.02.2007, 14:47

Die Objekte werden auf dem Freestore (per new) angelegt. Der kann wesentlich größer als der Stack sein, daher ist es ratsam große Objekte immer dynamisch zu erzeugen.

Virtual wird immer verwendet wenn eine Funktion von einer abgeleiteten Klasse überschrieben werden darf.
@D13_Dreinig

Werbeanzeige