C++ Programmierung · 10 min read · Oct 10, 2025

C/C++ Schritt-für-Schritt - Seite 16

16. Schritt-für-Schritt C/C++ — C++ Programmierung - Polymorphismus

Polymorphismus

| | 1. Funktionsüberladung

  1. Polymorphismus
  2. Arten von Polymorphismus
  3. Normale Mitgliedsfunktionen, die mit Zeigern aufgerufen werden
  4. Virtuelle Funktion
  5. Reine Funktion
  6. Zuweisung und Kopierinitialisierung
  7. Der KOPIER-Konstruktor
  8. ‘this’ Zeiger |

1. Funktionsüberladung

Wenn eine Funktion, deren Name sich durch die Argumente unterscheidet, als Funktionenpolymorphismus oder Funktionsüberladung bezeichnet wird.

| | // Ein Beispielprogramm zur Demonstration der Verwendung von Funktionsüberladung
#include
using namespace std;
void printline()
{
for(int i=0;i<=80; i++) cout << “-“;
}

void printline(int n)
{
for(int i =0 ;i<=n;i++) cout << “-“;
}

void printline(int n,char ch)
{
for(int i=0;i<=n; i++) cout << ch;
}

int main()
{
printline();
printline(5);
printline(10, ‘*’);
return 0;
}
|

Polymorphismus

Polymorphismus ist eines der entscheidenden Merkmale der OOP. Es bedeutet einfach einen Namen, mehrere Formen. Wir haben bereits gesehen, wie das Konzept des Polymorphismus mit überladenen Funktionen und Operatoren implementiert wird. Die überladenen Mitgliedsfunktionen werden durch Übereinstimmung der Argumente, sowohl Typ als auch Anzahl, ausgewählt. Der Compiler kennt diese Informationen zur Compile-Zeit und kann daher die geeignete Funktion für einen bestimmten Aufruf bereits zur Compile-Zeit auswählen. Dies wird als frühes Binden oder statisches Binden oder statische Verknüpfung bezeichnet. Auch bekannt als Compile-Zeit-Polymorphismus, bedeutet frühes Binden einfach, dass ein Objekt zur Compile-Zeit an seinen Funktionsaufruf gebunden wird.

Nun betrachten wir eine Situation, in der der Funktionsname und das Prototyp in beiden Klassen, Basis und abgeleitet, gleich sind. Zum Beispiel, betrachten Sie die folgenden Klassendefinitionen.

| | #include
using namespace std;
class A
{
int x;
public : void show();
};

class B : public A
{
int y;
public : void show();
};

int main()
{
B b;
b.show();
return 0;
} |

Wie verwenden wir die Mitgliedsfunktion show( ), um die Werte der Objekte sowohl der Klassen A als auch B auszugeben? Da das Prototyp von show( ) an beiden Stellen gleich ist, wird die Funktion nicht überladen und daher gilt kein statisches Binden. Tatsächlich weiß der Compiler nicht, was zu tun ist, und verschiebt die Entscheidung.

Es wäre schön, wenn die geeignete Mitgliedsfunktion während der Programmausführung ausgewählt werden könnte. Dies wird als Laufzeit-Polymorphismus bezeichnet. Wie könnte das geschehen? C++ unterstützt einen Mechanismus, der als virtuelle Funktion bekannt ist, um Laufzeit-Polymorphismus zu erreichen. Zur Laufzeit, wenn bekannt ist, welche Klassenobjekte in Betracht gezogen werden, wird die geeignete Version der Funktion aufgerufen.

Da die Funktion viel später nach der Kompilierung mit einer bestimmten Klasse verknüpft ist, wird dieser Prozess als spätes Binden bezeichnet. Es ist auch bekannt als dynamisches Binden oder dynamische Verknüpfung, da die Auswahl der geeigneten Funktion dynamisch zur Laufzeit erfolgt.

3. Arten von Polymorphismus

Polymorphismus ist von zwei Arten, nämlich.

| 1 | Compile-Zeit-Polymorphismus
Oder frühes Binden
Oder statisches Binden
Oder statische Verknüpfung-Polymorphismus. Ein Objekt wird zur Compile-Zeit an seinen Funktionsaufruf gebunden. |

| 2 | Laufzeit-Polymorphismus
Oder spätes Binden
Oder dynamisches Binden
Oder dynamische Verknüpfung-Polymorphismus. Die Auswahl und die geeignete Funktion erfolgt dynamisch zur Laufzeit. |

| | |

Dynamisches Binden ist eines der leistungsstärksten Merkmale von C++. Dies erfordert die Verwendung von Zeigern auf Objekte. Wir werden im Detail besprechen, wie die Objektzeiger und virtuellen Funktionen verwendet werden, um dynamisches Binden zu implementieren.

4. Normale Mitgliedsfunktionen, die mit Zeigern aufgerufen werden

Das folgende Programm besteht aus einer Basisklasse

| | / Normale Funktionen, die von einem Zeiger aufgerufen werden /
/ Polymorphismus mit Klassen (ohne Verwendung von VIRTUELLEM Polymorphismus /
#include
using namespace std;

class BASE
{
public :
void disp() { cout << “\nSie sind in der BASE-Klasse “; }
};

class DERIVED1 : public BASE
{
public :
void disp() { cout << “\nSie sind in der DERIVED1-Klasse”; }
};

class DERIVED2 : public BASE
{
public :
void disp() { cout << “\nSie sind in der DERIVED2-Klasse”; }
};

int main()
{
DERIVED1 d1;               // Objekt der abgeleiteten Klasse 1
DERIVED2 d2;               // Objekt der abgeleiteten Klasse 2
BASE *b;                      // Zeiger auf die Basisklasse

b=&d1;                          // Adresse von d1 im Zeiger b zuweisen
b->disp();                      // Aufruf von disp()
b=&d2;                          // Adresse von d2 im Zeiger b zuweisen
b->disp();                      // Aufruf von disp()
return 0;
} |

Das obige Programm demonstriert:

| | • Eine BASE-Klasse
• DERIVED1, DERIVED2 Klassen, die von BASE abgeleitet sind
• Objekte der abgeleiteten Klassen (d1,d2)
• Zeiger der BASE-Klasse b |
| | *Ausgabe
Sie sind in der BASE-Klasse
Sie sind in der BASE-Klasse |

5. Virtuelle Funktion

Virtuell bedeutet in der Wirkung existierend, aber nicht in der Realität.
Eine Mitgliedsfunktion kann als virtuelle Funktion deklariert werden, indem man die Mitgliedsfunktion mit dem Schlüsselwort virtuell voranstellt.

| | / Polymorphismus mit Klassen (virtueller Polymorphismus) /

#include
using namespace std;
class B
{
public :
void show(){ cout << “\nKlasse B Methode Show() “; }
virtual void disp() { cout << “\nKlasse B Methode disp()”; }
};

class D : public B
{
public :
void show(){cout << “\nKlasse D Methode Show() “; }
void disp(){ cout << “\nKlasse D Methode disp()”; }
};

int main()
{
D d1;
d1.show();
d1.disp(); // Mitglied der Basisklasse

B b;
D d;
B *Bptr;
Bptr = &b;
Bptr->show();
Bptr->disp(); // Mitglied der Basisklasse

Bptr=&d;
Bptr->show(); // Mitglieder der abgeleiteten Klasse
Bptr->disp(); // Mitglied der Basisklasse
return 0;
} |

| | Ausgabe Klasse D Methode Show()
Klasse D Methode disp() |

6. Reine Funktion

Eine Funktion, die in einer Basisklasse definiert ist und keine Definition in Bezug auf die abgeleitete Klasse hat, wird als reine Funktion bezeichnet. Einfach ausgedrückt ist eine reine Funktion eine virtuelle Funktion ohne Körper.

| | #include
using namespace std;
class B
{
public :
void show(){ cout << “\nKlasse B Methode Show() “; }
virtual void disp() = 0; // reine virtuelle Funktion
};

class D : public B
{
public :
void show(){cout << “\nKlasse D Methode Show() “; }
void disp(){ cout << “\nKlasse D Methode disp()”; }
};

int main()
{
D d1;
d1.show(); // O/P : Klasse D Methode show()
d1.disp(); // O/P : Klasse D Methode disp()

D d;
B *Bptr;

Bptr=&d;
Bptr->show(); // O/P : Klasse B Methode show()
Bptr->disp(); // O/P : Klasse D Methode disp()
return 0;
} |

Bptr -> show() * ist die standardmäßige ausführbare Funktion von Base Bptr -> disp() ist die standardmäßige ausführbare Funktion von Base, aber sie ist als virtuelle reine Funktion deklariert, sodass zur Laufzeit die disp()* der abgeleiteten Klasse aufgerufen wird.

| | / Programm zur Demonstration des Vorteils reiner virtueller Funktionen /

#include
using namespace std;
enum boolean { false, true };
class NAME
{
protected : char name[20];
public :
void getname()
{ cout << “Geben Sie den Namen ein:”; cin >> name; }

void showname()
{ cout << “\nDer Name ist “<< name; }

boolean virtual isGradeA() = 0; // reine virtuelle Funktion
};

class student : public NAME
{
private : float avg;
public :
void getavg()
{
cout << “\nGeben Sie den Durchschnitt des Schülers ein:”;
cin >> avg;
}

boolean isGradeA()
{ return (avg>=80) ? true : false ; }
};

class employee : public NAME
{
private : int sal;
public :
void getsal()
{ cout << “\nGeben Sie das Gehalt ein “; cin >> sal; }

boolean isGradeA()
{ return (sal>=10000) ? true : false ; }
};

int main()
{
NAME names[20]; // Anzahl der Zeiger auf Namen
student
s; // Zeiger auf Schüler
employee *e; // Zeiger auf Mitarbeiter
int n = 0; // Anzahl der Namen in der Liste
char choice;
do{
cout << “Geben Sie Schüler oder Mitarbeiter ein (s/e) “;
cin >> choice;
if(choice==’s’)
{
s = new student; // neuen Schüler erstellen
s->getname();
s->getavg();
names[n++]=s;
}
else
{
e = new employee; // neuen Mitarbeiter erstellen
e->getname();
e->getsal();
names[n++]=e;
}
cout << “Geben Sie einen weiteren ein (y/n) ?”; // noch einen machen
cin >> choice;
} while(choice==’y’);
for(int j=0; j{
names[j]->showname( );
if(names[j]->isGradeA( )==true)
cout << “Er ist eine Person der Note 1”;
}
return 0;
} |

7. Zuweisung und Kopierinitialisierung

Der C++-Compiler ist immer beschäftigt, Dinge in Ihrem Namen zu erledigen, die Sie nicht erledigen möchten. Wenn Sie die Kontrolle übernehmen, wird er sich Ihrem Urteil unterwerfen; andernfalls wird er die Dinge auf seine eigene Weise erledigen. Zwei wichtige Beispiele für diesen Prozess sind der Zuweisungsoperator und der Kopierkonstruktor.

Sie haben den Zuweisungsoperator wahrscheinlich schon viele Male verwendet, wahrscheinlich ohne viel darüber nachzudenken. Angenommen, a1 und a2 sind Objekte. Es sei denn, Sie sagen dem Compiler etwas anderes, wird die Anweisung.

a2 = a1;            // setze a2 auf den Wert von a1

Die Daten von a1 werden Mitglied für Mitglied in a2 kopiert. Dies ist die Standardaktion des Zuweisungsoperators, =.

Sie sind auch mit der Initialisierung von Variablen vertraut, die Initialisierung eines Objekts mit einem anderen Objekt, wie in

alpha a2(a1);    // initialisiere a2 mit dem Wert von a1

Verursacht eine ähnliche Aktion. Der Compiler erstellt ein neues Objekt, a2, und kopiert die Daten von a1, Mitglied für Mitglied, in a2. Dies ist die Standardaktion des Kopierkonstruktors.

Beide diese Standardaktivitäten werden kostenlos vom Compiler bereitgestellt. Wenn das Kopieren von Mitglied zu Mitglied das ist, was Sie wollen, müssen Sie keine weiteren Maßnahmen ergreifen. Wenn Sie jedoch möchten, dass die Zuweisung oder Initialisierung etwas Komplexeres tut, können Sie die Standardfunktionen überschreiben. Wir werden die Techniken zum Überladen des Zuweisungsoperators und des Kopierkonstruktors separat besprechen.

Überladen des Zuweisungsoperators

| | // Überladen des Zuweisungsoperators ( = )
#include
using namespace std;
class alpha
{
private:
int data;
public:
alpha() { } // Konstruktor ohne Argumente
alpha( int d )
{ data = d; } // Konstruktor mit einem Argument
void display()
{ cout << data; } // Daten anzeigen
alpha operator =(alpha & a) // überladener = Operator
{
data = a.data; // nicht automatisch erledigt
cout << “\n Zuweisungsoperator aufgerufen “;
return alpha(data);
}
};

int main()
{
alpha a1(37);
alpha a2;
a2 = a1; // Überladenes = aufrufen
cout << “\n a2 = “; a2.display(); // a2 anzeigen

alpha a3 = a2; // ruft NICHT = auf
cout << “\n a3 = “; a3.display(); // a3 anzeigen
return 0;
}
| | | Ausgabe: a2 = 37
a3 = 37 |

8. Der KOPIER-Konstruktor

Wie wir besprochen haben, können wir ein Objekt definieren und gleichzeitig mit dem Wert eines anderen Objekts initialisieren, mit zwei Arten von Anweisungen:

alpha a3(a2);                            // Kopierinitialisierung
alpha a3 = a2;                          // Kopierinitialisierung, alternative Syntax

Beide Definitionstile rufen einen Kopierkonstruktor auf: das ist ein Konstruktor, der sein Argument in ein neues Objekt kopiert. Der Standardkopierkonstruktor, der automatisch vom Compiler für jedes Objekt bereitgestellt wird, führt eine Mitglied-für-Mitglied-Kopie durch. Dies ist ähnlich zu dem, was der Zuweisungsoperator tut; der Unterschied besteht darin, dass der Kopierkonstruktor auch ein neues Objekt erstellt.

Das folgende Beispiel demonstriert den Kopierkonstruktor.

| | #include
using namespace std;
class alpha
{
private :
int data;
public:
alpha( ) { } // Konstruktor ohne Argumente
alpha(int d) { data = d; } // Konstruktor mit einem Argument
alpha(alpha& a) // Kopierkonstruktor
{
data = a.data;
cout << “\nKopierkonstruktor aufgerufen”;
}
void display( )
{ cout << data; }
void operator = (alpha& a) // überladener = Operator
{
data = a.data;
cout << “\nZuweisungsoperator aufgerufen”;
}
};

int main()
{
alpha a1( 37 );
alpha a2;

a2 = a1; // überladenes = aufrufen
cout << “ a2 = “; a2.display(); // a2 anzeigen

alpha a3( a1 ); // Kopierkonstruktor aufrufen

// alpha a3 = a1; // äquivalente Definition von a3

cout << “ a3 = “; a3.display(); // a3 anzeigen
return 0;
} |

Der obige Programm überlädt sowohl den Zuweisungsoperator als auch den Kopierkonstruktor.
Der überladene Zuweisungsoperator ist ähnlich wie im vorherigen Beispiel.

9. ‘this’ Zeiger

C++ verwendet ein einzigartiges Schlüsselwort namens this, um ein Objekt darzustellen, das eine Mitgliedsfunktion aufruft. This ist ein Zeiger, der auf das Objekt zeigt, für das die this Funktion aufgerufen wurde.

Dieser Zeiger führt einfach die Aufgabe aus, das Objekt selbst zurückzugeben.

Das folgende Programm definiert i, j Objekte und i wird mit dem Wert 5 zugewiesen und das gesamte Objekt von i wird durch seine Mitgliedsfunktion j zugewiesen.

| | #include
using namespace std;
class A
{
int a;
public:
A() { }
A(int x)
{ a = x; }
void display()
{
cout << a;
}
A get()
{

return *this; // Gibt sich selbst zurück

}
};
int main()
{
A i(5);
A j;

j = i.get();
j.display();
return 0;
} |

Ref: Objektorientierte Programmierung in Turbo C++: Robert Lafore

Share: X/Twitter LinkedIn

Erhalte neue Beiträge in deinem Posteingang.

Kein Spam. Jederzeit abmelden.