2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Im vorherigen Kapitel haben wir kurz einige Konzepte von Klassen und Objekten erwähnt. Lassen Sie uns sie nun gemeinsam überprüfen.
class ist das Schlüsselwort der definierenden Klasse, gefolgt vom Namen der definierenden Klasse, {} ist der Hauptteil der Klasse, gefolgt von „;“, und in der Klasse definierte Mitgliedsfunktionen werden standardmäßig erweitert (inline).
Ein Kapselungsformat, das in drei Typen unterteilt ist: öffentlich, privat und geschützt. Der Umfang der Zugriffsberechtigungen beginnt an der Position, an der das Zugriffsqualifikationsmerkmal erscheint, bis zum nächsten Zugriffsqualifikationsmerkmal. Wenn es kein nachfolgendes Zugriffsqualifikationsmerkmal gibt, wird es verwendet endet bei }, d. h. Klasse.
Die Klasse definiert einen neuen Bereich. Wenn Sie Mitglieder außerhalb der Klasse definieren, müssen Sie den Bereichsoperator :: verwenden, um anzugeben, zu welcher Klassendomäne das Mitglied gehört.
Das Folgende ist der Spielfilm///
• 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 wie ein Modell. Sie begrenzt nur die Mitgliedsvariablen
Es handelt sich um eine Deklaration und es wird kein Speicherplatz zugewiesen. Speicherplatz wird nur zugewiesen, wenn ein Objekt mithilfe einer Klasse instanziiert wird.
• Eine Klasse kann mehrere Objekte instanziieren. Die instanziierten Objekte belegen tatsächlichen physischen Raum und speichern Klassenmitgliedsvariablen.
Um es in einem Satz zusammenzufassen: Nur wenn Objekte mit Klassen instanziiert werden, wird Platz zugewiesen (so wie der Bau eines Hauses mit Zeichnungen Platz belegt).
- class Date
- {
- public:
- void Init(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
Im obigen Code werden nur die Mitgliedsvariablen in private definiert und kein neuer Bereich geöffnet
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
Dann werden zwei neue Objekte instanziiert, sodass ein neuer Raum geöffnet wird.
• Das erste Mitglied befindet sich am Adressoffset 0 von der Struktur.
• Andere Mitgliedsvariablen sollten Adressen zugeordnet werden, die ganzzahlige Vielfache einer bestimmten Zahl (Logarithmus) sind.
• Hinweis: Logarithmus = der kleinere Wert aus dem Standardlogarithmus des Compilers und der Größe des Mitglieds.
• Der Standardlogarithmus in VS ist 8
• Die Gesamtgröße der Struktur beträgt: ein ganzzahliges Vielfaches der maximalen paarweisen Anzahl (der größte aller Variablentypen und der kleinste Standardpaarungsparameter).
• Wenn eine Struktur verschachtelt ist, wird die verschachtelte Struktur an einem ganzzahligen Vielfachen ihres maximalen Logarithmus und der Gesamtgröße der Struktur ausgerichtet
Es ist ein ganzzahliges Vielfaches aller größten Logarithmen (einschließlich der Logarithmen verschachtelter Strukturen).
Dies ist, was wir in der C-Sprache wissen. Nehmen Sie die folgenden Fragen als Beispiel:
- // 计算⼀下A实例化的对象是多⼤?
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
- int _i;
- };
Es gibt keine aufgerufene Funktion, also schauen Sie sich einfach die Mitgliedsvariablen an. _ch ist ein char-Zeichentyp mit einer Größe von 1 Byte. _i ist ein int-Typ mit einer Größe von 4 Bytes Ausrichtungsnummer, also _ Setzen Sie ch an die Position mit dem Index 0, lassen Sie drei Leerzeichen leer und geben Sie dann _i ein. Es belegt insgesamt 8 Bytes Speicherplatz von 0 bis 7, was einem ganzzahligen Vielfachen der maximalen Ausrichtungsnummer entspricht . Es erfüllt die Bedingungen, daher lautet die Antwort auf diese Frage 8 .
Und was ist, wenn es diese beiden Fragen sind?
- class B
- {
- public:
- void Print()
- {
- //...
- }
- };
-
- class C
- {
- };
Wir können feststellen, dass es in Klasse B eine leere Funktion und in Klasse C keine Mitglieder gibt. In der Sprache C belegen wir, dass beide Klassen 1 Byte Platz belegen.
• DEs gibt zwei Mitgliedsfunktionen, Init und Print, in der ate-Klasse. Wenn d1 dann Init und Print aufruft, gibt es keinen Unterschied zwischen verschiedenen Objekten.
Woher weiß die Funktion beim Drucken der Funktion, ob sie auf das d1-Objekt oder das d2-Objekt zugreifen soll?Dann werden wir hier sehen, dass C++ gibt
Ein impliziter this-Zeiger löst das Problem hier
• 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 Klasse „Init of Date“ lautet beispielsweise: void Init(Date* const this, int year,
int Monat, int Tag)
• Beim Zugriff auf Mitgliedsvariablen in den Mitgliedsfunktionen einer Klasse erfolgt der Zugriff im Wesentlichen über den this-Zeiger. Wenn beispielsweise _year in der Init-Funktion ein Wert zugewiesen wird, geschieht dies.
>_year = Jahr;
• C++ legt fest, dass dieser Zeiger nicht explizit an die Position von Aktualparametern und Formalparametern geschrieben werden kann (der Compiler wird dies während der Kompilierung behandeln), dieser Zeiger kann jedoch explizit im Funktionskörper verwendet werden.
Zum Beispiel
- class Date
- {
- public:
- // void Init(Date* const this, int year, int month, int day)
- void Init(int year, int month, int day)
- {
- // 编译报错:error C2106: “=”: 左操作数必须为左值
- // this = nullptr;
- // this->_year = year;
- _year = year;
- this->_month = month;
- this->_day = day;
- }
- void Print()
- {
- cout << _year << "/" << _month << "/" << _day << endl;
- }
- private:
- // 这⾥只是声明,没有开空间
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- // Date类实例化出对象d1和d2
- Date d1;
- Date d2;
- // d1.Init(&d1, 2024, 3, 31);this指针
- d1.Init(2024, 3, 31);
-
- // d1.Init(&d2, 2024, 7, 5);
- d2.Init(2024, 7, 5);
- //不能自己写
- return 0;
- }
Zum Schreiben tatsächlicher Parameter in der Init-Funktion fügt C++ der ersten Variablen der Funktion eine unsichtbare Konstante hinzu, diesen Zeiger (der nicht geändert werden kann), wie in den Kommentaren in der Funktion gezeigt.
Solange der Nullzeiger nicht dereferenziert ist, wird kein Fehler gemeldet.
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Obwohl p ein Nullzeiger zugewiesen ist und auf die Print-Funktion zeigt, wird er nicht dereferenziert, sodass hier kein Fehler gemeldet wird. Und dieses Programm:
- class A
- {
- public:
- void Print()
- {
- cout << "A::Print()" << endl;
- cout << _a << endl;//这个地方多了一句
- }
- private:
- int _a;
- };
-
- int main()
- {
- A* p = nullptr;
- p->Print();
- return 0;
- }
Im Vergleich zum vorherigen Programm gibt es nur noch einen Satz cout << _a << endl, aber zu diesem Zeitpunkt zeigt der Nullzeiger auf eine Mitgliedsvariable und der Nullzeiger wird dereferenziert, sodass das Programm einen Fehler meldet.
Die Standard-Member-Funktion ist eine Member-Funktion, die nicht explizit vom Benutzer implementiert wird und vom Compiler automatisch generiert wird. Sie wird als Standard-Member-Funktion bezeichnet. Wenn wir eine Klasse nicht schreiben, generiert der Compiler standardmäßig die folgenden 6 Standard-Memberfunktionen, nämlich die folgenden 6
Der Konstruktor ist eine spezielle Mitgliedsfunktion. Es ist zu beachten, dass der Name des Konstruktors zwar Konstruktor heißt, die Hauptaufgabe des Konstruktors jedoch nicht darin besteht, Platz zum Erstellen von Objekten zu schaffen (das lokale Objekt, das wir häufig verwenden, ist der Platz beim Stapeln). Frame wird erstellt) ), stattdessen wird das Objekt initialisiert, wenn das Objekt instanziiert wird. Die Essenz besteht darin, die Initialisierung durch die Init-Funktion zu ersetzen.
1. Der Funktionsname ist derselbe wie der Klassenname.
2. Kein Rückgabewert. (Es ist nicht nötig, „void“ zu schreiben)
3. Wenn das Objekt instanziiert wird, ruft das System automatisch den entsprechenden Konstruktor auf.
4. Konstruktoren können überlastet sein.
5. Wenn in der Klasse kein explizit definierter Konstruktor vorhanden ist, generiert der C++-Compiler automatisch einen Parameterlosen Standardkonstruktor. Sobald der Benutzer ihn explizit definiert, generiert der Compiler ihn nicht mehr.
Die ersten fünf Punkte sind relativ einfach, geben wir ein Beispiel:
- class Date
- {
- public:
- //1.⽆参构造函数,无需写void
- Date()//若不写,则构造函数会写出这一中无参构造函数
- {
- _year = 1;
- _month = 1;
- _day = 1;
- }
- //2.全缺省构造函数
- Date(int year = 1, int month = 1, int day = 1)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- }
-
- int main()
- {
- Date d1;//此时相当于已经自动调用了Init函数
- Date d2;
-
- return 0;
- }
Im Programm sind zwei Konstruktoren geschrieben, einer ist ein parameterloser Konstruktor und der andere ist ein vollständiger Standardkonstruktor. Wenn wir keine Funktion schreiben, schreibt das Programm bis zu einem Parameterlosen Konstruktor Zwei Funktionen sind überladene Funktionen. Aufgrund der Mehrdeutigkeit des Aufrufs können die beiden Funktionen nicht gleichzeitig existieren.
6. Parameterlose Konstruktoren, vollständige Standardkonstruktoren und vom Compiler standardmäßig generierte Konstruktoren, wenn wir keinen Konstruktor schreiben, werden alle als Standardkonstruktoren bezeichnet. Allerdings existiert nur eine dieser drei Funktionen und kann nicht gleichzeitig existieren. Obwohl der parameterlose Konstruktor und der vollständige Standardkonstruktor eine Funktionsüberladung darstellen, kommt es beim Aufruf zu Mehrdeutigkeiten. Es sollte beachtet werden, dass viele Studenten denken, dass der Standardkonstruktor der vom Compiler generierte Standardkonstruktor ist. Tatsächlich sind der Parameterloskonstruktor und der Vollstandardkonstruktor auch Standardkonstruktoren, die aufgerufen werden können, ohne tatsächliche Werte zu übergeben Parameter. Es wird als Standardkonstruktion bezeichnet.
Die sechste Funktion ist komplizierter: Parameterlose Konstruktoren, Standardkonstruktoren und Konstruktoren, die vom Compiler standardmäßig generiert werden, wenn wir keinen Konstruktor schreiben, werden alle als Standardkonstruktoren bezeichnet, und es kann nur einen geben. Darf den Standardkonstruktor nicht verstehen als vom Compiler standardmäßig generierter Konstruktor!
7. Wenn wir es nicht schreiben, stellt der vom Compiler standardmäßig generierte Konstruktor keine Anforderungen an die Initialisierung integrierter Typ-Member-Variablen. Mit anderen Worten, es ist ungewiss, ob er initialisiert wird oder nicht, es hängt vom Compiler ab . Für benutzerdefinierte Mitgliedsvariablen ist es erforderlich, den Standardkonstruktor dieser Mitgliedsvariablen aufzurufen, um sie zu initialisieren. Wenn diese Mitgliedsvariable keinen Standardkonstruktor hat, wird ein Fehler gemeldet. Wenn wir diese Mitgliedsvariable initialisieren möchten, müssen wir eine Initialisierungsliste verwenden, um das Problem zu lösen. Wir werden die Initialisierungsliste im nächsten Kapitel ausführlich erläutern .
Die Funktion des Destruktors ist die des Konstruktors. Der Destruktor führt die Zerstörung des Objekts selbst nicht durch. Wenn die Funktion endet, wird der Stapelrahmen zerstört Wir müssen es nicht kontrollieren. C++ schreibt vor, dass Objekte beim Zerstören automatisch aufgerufen werden, um die Bereinigung und Freigabe der Ressourcen im Objekt abzuschließen. Die Funktion des Destruktors ähnelt der Destroy-Funktion, die wir zuvor in Stack implementiert haben. Tatsächlich gibt es keine Ressourcen, die freigegeben werden müssen. Date benötigt also keinen Destruktor Der Destruktor ist genau wie die Destroy-Funktion. Er sollte für Datenstrukturen verwendet werden, die nicht lokale Objekte wie Stapel und Warteschlangen erfordern, um Ressourcen zu beantragen.
1. Dem Destruktornamen wird das Zeichen ~ vor dem Klassennamen vorangestellt. (Ähnlich der bitweisen Negation in der C-Sprache)
2. Keine Parameter und kein Rückgabewert. (Dies ähnelt der Struktur und es ist nicht erforderlich, Lücken hinzuzufügen.)
3. Eine Klasse kann nur einen Destruktor haben. Wenn nicht explizit definiert, generiert das System automatisch einen Standarddestruktor.
4. Wenn der Objektlebenszyklus endet, ruft das System automatisch den Destruktor auf.
5. Ähnlich wie beim Konstruktor werden die integrierten Typmitglieder nicht verarbeitet, wenn wir keinen automatisch vom Compiler generierten Destruktor schreiben. Benutzerdefinierte Typmitglieder rufen ihre Destruktoren auf.
6. Es ist auch zu beachten, dass beim expliziten Schreiben des Destruktors auch der Destruktor des benutzerdefinierten Typmitglieds aufgerufen wird. Das heißt, der Destruktor des benutzerdefinierten Typmitglieds wird unabhängig von der Situation automatisch aufgerufen.
7. Wenn in der Klasse keine Ressourcen beantragt werden, muss der Destruktor nicht geschrieben werden und der vom Compiler generierte Standarddestruktor, z. B. Date, kann direkt verwendet werden. Es ist nicht erforderlich, einen Destruktor wie MyQueue zu schreiben. Wenn jedoch eine Ressourcenanwendung vorhanden ist, müssen Sie den Destruktor selbst schreiben, da dies sonst zu Ressourcenverlusten führt.
8. Für mehrere Objekte in einer lokalen Domäne schreibt C++ vor, dass die später definierten zuerst zerstört werden.
Wie der obige Konstruktor ist auch der von C++ automatisch generierte Standarddestruktor für uns im Allgemeinen keine große Hilfe, in einigen seltenen Fällen ist er jedoch praktischer, z. B. bei der Verwendung von zwei Stapeln zum Implementieren einer Warteschlange:
- public:
- Stack(int n = 4)//相当于Init
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc fail!");
- return;
- }
- _capacity = n;
- _top = 0;
- }
- ~Stack()//相当于Destroy
- {
- cout << "~Stack()" << endl;
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
-
- class MyQueue
- {
- public:
- //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
- // 显⽰写析构,也会⾃动调⽤Stack的析构
-
- private:
- Stack pushst;
- Stack popst;
- };
Zu diesem Zeitpunkt ruft der Compiler in MyQueue automatisch den Konstruktor (Initialisierung) und den Destruktor (Zerstörung) des Stapels auf.