Technologieaustausch

C-Klassen und -Objekte (1)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Inhaltsverzeichnis

Prozessorientiert und objektorientiert

Verfahrensprogrammierung

Objekt orientierte Programmierung

1. Definition der Klasse

Klassendefinitionsformat

Klassendomäne

2. Klassenzugriffsqualifikatoren und Kapselung

Zugriffsqualifizierer

Verkapselung

3. Instanziierung

Instanziierungskonzept

Objektgröße

4. dieser Zeiger

Eigenschaften dieses Zeigers


Prozessorientiert und objektorientiert

  • Die C-Sprache ist eine typische prozessorientierte Programmiersprache. Der Entwurf des Programms konzentriert sich hauptsächlich auf Funktionen und Datenstrukturen, wobei der Schwerpunkt auf dem Implementierungsprozess von Funktionen liegt.
  • C++ unterstützt sowohl prozedurale als auch objektorientierte Programmierung. Zu den Kernkonzepten der objektorientierten Programmierung gehören Klassen, Objekte, Kapselung, Vererbung und Polymorphismus usw., wodurch die Organisation und das Design des Programms besser mit dem realen Modell übereinstimmen und die Wartbarkeit und Skalierbarkeit des Codes verbessert werden.

Verfahrensprogrammierung

Prozessorientierte Programmierung ist eine prozesszentrierte Programmieridee. Bei der prozessorientierten Programmierung wird ein Programm als eine Sammlung von Funktionen oder Prozeduren betrachtet, die in einer bestimmten Reihenfolge ausgeführt werden, um eine bestimmte Aufgabe abzuschließen.

Vorteil:

  • Leistungseffizient : Da die prozessorientierte Programmierung Aufgaben direkt entsprechend dem Prozess ausführt und keine übermäßige Objekterstellung und -verwaltung erfordert, weist sie in einigen Szenarien mit höheren Leistungsanforderungen, wie z. B. zugrunde liegende Systemprogrammierung, eingebettete Programmierung usw., eine höhere Ausführungseffizienz auf. Beispielsweise kann im Betriebssystemkernel die prozessorientierte Programmierung genutzt werden, um die Leistung für die Implementierung von Funktionen wie Prozessplanung und Speicherverwaltung besser zu optimieren.
  • klare Logik : Für eine einfache Programmlogik wird sie Schritt für Schritt entsprechend dem Prozess implementiert. Die logische Struktur des Codes ist klar und leicht zu verstehen, leicht zu verstehen und zu warten. Beispielsweise kann ein einfaches Programm, das die Summe zweier Zahlen berechnet, mithilfe der prozessorientierten Programmierung direkt eine Funktion zur Berechnung definieren.

Mangel:

  • Schlechte Wartbarkeit : Wenn die Programmgröße zunimmt und die Funktionen komplexer werden, kann es schwierig werden, auf prozeduraler Programmierung ausgerichteten Code zu warten und zu erweitern. Da die Kopplung zwischen verschiedenen Funktionen hoch ist, kann sich die Änderung einer Funktion auf andere verwandte Funktionen auswirken.
  • Geringe Wiederverwendbarkeit des Codes: Die Wiederverwendung von Code wird normalerweise durch Funktionsaufrufe erreicht, aber bei komplexen Funktionsmodulen ist die Wiederverwendung schwieriger und die Funktionen können nicht gut gekapselt und abstrahiert werden.

Objekt orientierte Programmierung

Objektorientierte Programmierung ist eine objektzentrierte Programmieridee. Objekte sind Entitäten, die Daten (Eigenschaften) und Methoden (Verhalten) enthalten, die mit diesen Daten arbeiten. Durch die Kapselung verwandter Daten und Methoden in einem Objekt wird die Integration von Daten und Operationen erreicht.

Vorteil:

  • Hohe Wartbarkeit : Kapseln Sie Funktionen in Objekten, verbergen Sie die interne Implementierung der Objekte vor der Außenwelt und reduzieren Sie die Kopplung zwischen Modulen. Wenn eine Funktion geändert werden muss, muss nur die interne Implementierung des entsprechenden Objekts geändert werden, ohne dass andere, nicht verwandte Teile davon betroffen sind. Wenn Sie beispielsweise in einer grafischen Schnittstellenanwendung die Funktion einer Schaltfläche ändern möchten, müssen Sie nur die entsprechende Methode des Schaltflächenobjekts ändern, ohne dass sich dies auf andere Schnittstellenelemente auswirkt.
  • Starke Wiederverwendbarkeit des Codes : Die Wiederverwendung und Erweiterung von Code kann leicht durch Vererbung, Polymorphismus und andere Funktionen erreicht werden.Erstellen Sie beispielsweise eine BasisklasseShape(Form) und dann ableitenCircle(runden),Rectangle(Rechteck) und andere Unterklassen können die Attribute und Methoden der Basisklasse wiederverwenden und bestimmte Erweiterungen vornehmen.
  • Gute Flexibilität: Objektorientierte Programmierung unterstützt Polymorphismus, der es dem Programm ermöglicht, entsprechende Methoden entsprechend dem tatsächlichen Objekttyp zur Laufzeit dynamisch auszuwählen und auszuführen, wodurch die Flexibilität und Skalierbarkeit des Programms erhöht wird.

Mangel:

  • Leistungsaufwand: Da die Objekterstellung, Methodenaufrufe und andere Vorgänge einen gewissen Overhead erfordern, kann dies in einigen Szenarien mit extrem hohen Leistungsanforderungen die Ausführungseffizienz des Programms beeinträchtigen.
  • Hohe Lernkosten: Die Konzepte und Funktionen der objektorientierten Programmierung sind relativ komplex, was es für Anfänger schwierig macht, sie zu erlernen und zu verstehen.

1. Definition der Klasse

Klassendefinitionsformat

• class ist das Schlüsselwort, das die Klasse definiert, Data ist der Name der Klasse und {} ist der Hauptteil der Klasse. Beachten Sie, dass das abschließende Semikolon am Ende der Klassendefinition nicht weggelassen werden darf. Die Inhalte im Klassenkörper werden als Mitglieder der Klasse bezeichnet: Variablen in der Klasse werden als Attribute oder Mitgliedsvariablen der Klasse bezeichnet; Funktionen in der Klasse werden als Methoden oder Mitgliedsfunktionen der Klasse bezeichnet.

• Um Mitgliedsvariablen zu unterscheiden, ist es im Allgemeinen üblich, der Mitgliedsvariablen einen speziellen Bezeichner hinzuzufügen, z. B. vor oder nach der Mitgliedsvariablen mit _ oder m zu beginnen. Beachten Sie, dass dies in C++ nicht obligatorisch ist, sondern lediglich einige Konventionen .

• In C++ kann struct auch Klassen definieren. Gleichzeitig wurde struct zu einer Klasse erweitert. Die offensichtliche Änderung besteht darin, dass Funktionen in struct definiert werden können Es wird empfohlen, Klassen zum Definieren von Klassen zu verwenden.

• In einer Klasse definierte Mitgliedsfunktionen sind standardmäßig inline.

  1. class Date
  2. {
  3. public:
  4. void Init(int year, int month, int day)
  5. {
  6. _year = year;
  7. _month = month;
  8. _day = day;
  9. }
  10. private:
  11. // 为了区分成员变量,⼀般习惯上成员变量
  12. // 会加⼀个特殊标识,如_ 或者 m开头
  13. int _year; // year_ m_year
  14. int _month;
  15. int _day;
  16. };
  17. int main()
  18. {
  19. Date d;
  20. d.Init(2024, 3, 31);
  21. return 0;
  22. }

Klassendomäne

• Die Klasse definiert einen neuen Bereich. Wenn Sie Mitglieder außerhalb der Klasse definieren, müssen Sie den ::scope-Operator verwenden, um anzugeben, zu welcher Klassendomäne das Mitglied gehört.

• Die Klassendomäne beeinflusst die Suchregeln der Kompilierung. Wenn Init im folgenden Programm die Klassendomäne Stack nicht angibt, behandelt der Compiler Init als globale Funktion. Die Deklaration/Definition von Mitgliedern wie Arrays ist dann nicht möglich gefunden, wird ein Fehler gemeldet. Wenn Sie den Klassendomänenstapel angeben, wissen Sie, dass Init eine Mitgliedsfunktion ist. Wenn Mitglieder wie Arrays in der aktuellen Domäne nicht gefunden werden können, werden sie in der Klassendomäne durchsucht.

  1. #include<iostream>
  2. using namespace std;
  3. class Stack
  4. {
  5. public:
  6. // 成员函数
  7. void Init(int n = 4);
  8. private:
  9. // 成员变量
  10. int* array;
  11. size_t capacity;
  12. size_t top;
  13. };
  14. // 声明和定义分离,需要指定类域
  15. void Stack::Init(int n)
  16. {
  17. array = (int*)malloc(sizeof(int) * n);
  18. if (nullptr == array)
  19. {
  20. perror("malloc申请空间失败");
  21. return;
  22. }
  23. capacity = n;
  24. top = 0;
  25. }
  26. int main()
  27. {
  28. Stack st;
  29. st.Init();
  30. return 0;
  31. }

2. Klassenzugriffsqualifikatoren und Kapselung

Zugriffsqualifizierer

• C++ ist eine Möglichkeit, Kapselung zu implementieren, indem Klassen verwendet werden, um die Eigenschaften und Methoden eines Objekts zu kombinieren, um das Objekt vollständiger zu machen und seine Schnittstellen über Zugriffsberechtigungen selektiv externen Benutzern bereitzustellen.

• Auf von public geänderte Mitglieder kann direkt außerhalb der Klasse zugegriffen werden. Auf geschützte und private geänderte Mitglieder kann nicht direkt außerhalb der Klasse zugegriffen werden, und ihre Unterschiede werden später im Vererbungskapitel widergespiegelt.

• Der Umfang der Zugriffsberechtigungen beginnt an der Position, an der das Zugriffsqualifikationsmerkmal erscheint, bis zum nächsten Zugriffsqualifikationsmerkmal. Wenn es kein nachfolgendes Zugriffsqualifikationsmerkmal gibt, endet der Geltungsbereich bei }, also der Klasse.

• Wenn ein Klassendefinitionsmitglied nicht durch einen Zugriffsqualifizierer geändert wird, wird es standardmäßig auf „privat“ gesetzt.

• struct ist standardmäßig öffentlich

• Im Allgemeinen werden Mitgliedsvariablen auf privat/geschützt beschränkt und Mitgliedsfunktionen, die von anderen verwendet werden müssen, werden öffentlich gemacht.

Verkapselung

Die drei Hauptmerkmale der Objektorientierung: Kapselung, Vererbung und Polymorphismus.

In der Klassen- und Objektphase untersuchen wir hauptsächlich die Kapselungseigenschaften von Klassen. Was ist also Kapselung?

Kapselung: Kombinieren Sie Daten und Methoden zum Betrieb von Daten organisch, verbergen Sie die Eigenschaften und Implementierungsdetails des Objekts und legen Sie nur die Schnittstelle für die Interaktion mit dem Objekt offen.

Bei der Kapselung handelt es sich im Wesentlichen um eine Art der Verwaltung, die Benutzern die Verwendung von Klassen erleichtert. Beispiel: Bei einem komplexen Gerät wie einem Computer stehen dem Benutzer lediglich die Ein- und Ausschalttasten, die Tastatureingabe, der Monitor, die USB-Buchse usw. zur Verfügung, sodass der Benutzer mit dem Computer interagieren und tägliche Aufgaben erledigen kann. Tatsächlich besteht die eigentliche Arbeit des Computers jedoch aus der CPU, der Grafikkarte, dem Speicher und anderen Hardwarekomponenten.

Computerbenutzer müssen sich keine Gedanken über die internen Kernkomponenten machen, z. B. wie die Schaltkreise auf der Hauptplatine angeordnet sind, wie die CPU konstruiert ist usw. Benutzer müssen lediglich wissen, wie und wie sie den Computer einschalten über Tastatur und Maus mit dem Computer interagieren. Wenn Computerhersteller das Werk verlassen, platzieren sie daher außen eine Hülle, um die internen Implementierungsdetails zu verbergen, und stellen nur Netzschalter, Maus- und Tastaturanschlüsse nach außen, damit Benutzer mit dem Computer interagieren können.

Um die Kapselung in der Sprache C++ zu implementieren, können Daten und Methoden zum Betreiben von Daten organisch über Klassen kombiniert werden. Mithilfe von Zugriffsrechten können die internen Implementierungsdetails von Objekten ausgeblendet und gesteuert werden, welche Methoden direkt außerhalb der Klasse verwendet werden können.

3. Instanziierung

Instanziierungskonzept

• Der Prozess der Erstellung eines Objekts im physischen Speicher mithilfe eines Klassentyps wird als Klasseninstanziierung bezeichnet.

• Eine Klasse ist eine abstrakte Beschreibung eines Objekts. Sie ist so etwas wie ein Modell, das die Mitgliedsvariablen der Klasse begrenzt und keinen Speicherplatz zuweist, wenn ein Objekt instanziiert wird Klasse.

• Eine Klasse kann mehrere Objekte instanziieren. Die instanziierten Objekte belegen tatsächlichen physischen Raum und speichern Klassenmitgliedsvariablen. Zum Beispiel: Das Instanziieren von Objekten aus einer Klasse ist wie die Verwendung von Architekturentwurfszeichnungen, um in der Realität ein Haus zu bauen. Eine Klasse ist wie eine Entwurfszeichnung, wie viele Räume es gibt, welche Raumgröße und Funktionen usw., aber da ist kein physisches Objekt. Auch wenn ein Gebäude existiert, kann es nicht in Menschen bewohnt werden. Dieselbe Klasse ähnelt einer Konstruktionszeichnung und kann keine Daten speichern. Das instanziierte Objekt weist physischen Speicher zum Speichern von Daten zu.

Objektgröße

Analysieren Sie, welche Mitglieder im Klassenobjekt enthalten sind. Jedes von einer Klasse instanziierte Objekt verfügt über einen unabhängigen Datenraum, daher muss das Objekt Mitgliedsvariablen enthalten. Sind also Mitgliedsfunktionen enthalten? Erstens handelt es sich nach der Kompilierung der Funktion um einen Abschnitt mit Anweisungen, die nicht im Objekt gespeichert werden können. Diese Anweisungen werden in einem separaten Bereich (Codesegment) gespeichert. Wenn sie also im Objekt gespeichert werden müssen, können sie nur gespeichert werden Zeiger auf Memberfunktionen. Lassen Sie uns noch einmal analysieren: Ist es notwendig, Zeiger im Objekt zu speichern? Date instanziiert zwei Objekte d1 und d2. Sowohl d1 als auch d2 haben ihre eigenen unabhängigen Mitgliedsvariablen _year/_month/_day, aber die Mitgliedsfunktion Init/Print Die Zeiger von d1 und d2 sind gleich, daher ist das Speichern in Objekten verschwendet. Wenn Sie Date verwenden, um 100 Objekte zu instanziieren, wird der Zeiger der Mitgliedsfunktion 100 Mal gespeichert, was zu verschwenderisch ist. Tatsächlich muss der Funktionszeiger nicht gespeichert werden. Die aufrufende Funktion wird in eine Assembly-Anweisung kompiliert. Tatsächlich muss der Compiler die Adresse der Funktion beim Kompilieren und Verknüpfen finden , nicht zur Laufzeit. Zur Laufzeit wird nur dynamischer Polymorphismus gefunden, und Funktionsadressen müssen gespeichert werden.

Oben haben wir analysiert, dass nur Mitgliedsvariablen in Objekten gespeichert werden. C++ schreibt vor, dass von Klassen instanziierte Objekte auch den Speicherausrichtungsregeln entsprechen müssen.

Regeln zur Speicherausrichtung

Die Speicherausrichtungsregeln sind genau die gleichen wie in der C-SpracheReferenzartikel:C-Sprache zur Berechnung der Speicherausrichtung

Das erste Mitglied befindet sich am Adressoffset 0 von der Struktur.

• Andere Mitgliedsvariablen sollten Adressen zugeordnet werden, die ganzzahlige Vielfache einer bestimmten Zahl (Ausrichtungsnummer) sind.

• Hinweis: Logarithmus = der kleinere Wert aus der Standardausrichtungsnummer des Compilers und der Größe des Elements.

•Der Standardlogarithmus in VS ist 8

• Die Gesamtgröße der Struktur beträgt: ein ganzzahliges Vielfaches der maximalen Alignment-Nummer (der größte aller Variablentypen und der kleinste Standard-Alignment-Parameter).

• Wenn eine Struktur verschachtelt ist und die verschachtelte Struktur auf ein ganzzahliges Vielfaches ihres eigenen maximalen Logarithmus ausgerichtet ist, entspricht die Gesamtgröße der Struktur der maximalen Ausrichtungszahl aller Zahlen (einschließlich der Ausrichtung der verschachtelten Struktur).

Wenn keine Mitgliedsvariable vorhanden ist, muss 1 Byte angegeben werden, denn wenn nicht einmal ein Byte angegeben ist, wie kann dann gezeigt werden, dass das Objekt existiert hat? Daher wird hier 1 Byte lediglich zur Platzhalteridentifizierung der Existenz des Objekts angegeben.

4. dieser Zeiger

Eigenschaften dieses Zeigers

Es gibt zwei Mitgliedsfunktionen, Init und Print, in der Date-Klasse. Wenn d1 also die Init- und Print-Funktionen aufruft, weiß die Funktion, ob sie auf das d1-Objekt zugreifen soll das d2-Objekt?Dann werden wir hier sehen, dass C++ einen impliziten this-Zeiger bereitstellt, um das Problem hier zu lösen.

• Nachdem der Compiler kompiliert hat, fügen die Mitgliedsfunktionen der Klasse standardmäßig einen Zeiger des aktuellen Klassentyps, diesen Zeiger genannt, an der ersten Position des formalen Parameters hinzu. Der eigentliche Prototyp der Init of Date-Klasse ist beispielsweise:void Init(Datum* const this, int Jahr, int Monat, int Tag) • Beim Zugriff auf Mitgliedsvariablen in Klassenmitgliedsfunktionen erfolgt der Zugriff im Wesentlichen über diesen Zeiger. Wenn beispielsweise _year in der Init-Funktion ein Wert zugewiesen wird, ist dies der Fall -&gt;_year = Jahr;

• C++ legt fest, dass dieser Zeiger nicht explizit an die Position von Aktualparametern und formalen Parametern geschrieben werden kann (der Compiler wird ihn während der Kompilierung verarbeiten), dieser Zeiger kann jedoch explizit im Funktionskörper verwendet werden.

  1. #include<iostream>
  2. using namespace std;
  3. class Date
  4. {
  5. public:
  6. // void Init(Date* const this, int year, int month, int day)
  7. void Init(int year, int month, int day)
  8. {
  9. // 编译报错:error C2106: “=”: 左操作数必须为左值
  10. // this = nullptr;
  11. // this->_year = year;
  12. _year = year;
  13. this->_month = month;
  14. this->_day = day;
  15. }
  16. void Print()
  17. {
  18. cout << _year << "/" << _month << "/" << _day << endl;
  19. }
  20. private:
  21. // 这⾥只是声明,没有开空间
  22. int _year;
  23. int _month;
  24. int _day;
  25. };
  26. int main()
  27. {
  28. // Date类实例化出对象d1和d2
  29. Date d1;
  30. Date d2;
  31. d1.Init(2024, 7, 1); // d1.Init(&d1, 2024, 7, 1);
  32. d1.Print(); // d1.Print(&d1);
  33. d2.Init(2024, 7, 10); // d2.Init(&d2, 2024, 7, 10);
  34. d2.Print(); // d2.Print(&d2);
  35. return 0;
  36. }