[C++-Erste Schritte] Klassen und Objekte (Teil 1)


Fügen Sie hier eine Bildbeschreibung ein


1. Vorläufiges Verständnis von prozessorientiert und objektorientiert

 Die C-Sprache ist prozessorientiert , konzentriert sich auf den Prozess , analysiert die Schritte zur Lösung des Problems und löst das Problem schrittweise durch Funktionsaufrufe.
Fügen Sie hier eine Bildbeschreibung ein


 C++ ist objektorientiert und konzentriert sich auf Objekte . Es unterteilt eine Sache in verschiedene Objekte und basiert auf der Interaktion zwischen Objekten.
Fügen Sie hier eine Bildbeschreibung ein


2. Einführung von Klassen

In C-Sprachstrukturen können nur Variablen definiert werden. In C++ können beim Upgrade einer Struktur auf eine Klasse nicht nur Variablen, sondern auch Funktionen definiert werden.. Beispiel: In der Anfangsphase der Datenstruktur, als der Stapel in der C-Sprache implementiert wurde, konnten in der Struktur nur Variablen definiert werden ; jetzt , wenn er in C++ implementiert ist, werden Sie feststellen, dass auch Funktionen in der Struktur definiert werden können.

Demonstration des CPP-Codes :

struct Stack
{
    
    
	//函数
	void Init()
	{
    
    
		a = nullptr;
		capacity = top = 0;
	}

	void Push(int x)
	{
    
    
		a[top] = x;
		top++;
	}
	// ....其他函数
	
	//变量
	int* a;
	int top;
	int capacity;
};

Aber inCPP bevorzugt die Verwendung von Klasse anstelle von StrukturWerfen wir einen Blick auf die Klassennutzung (der Unterschied zwischen den beiden wird später vorgestellt).


3. Klassendefinition

class ClassName
{
    
    
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

Klassezum Definieren von KlassenSchlüsselwörterClassName ist der Name der Klasse und { } ist der Hauptteil der Klasse.Beachten Sie, dass das Semikolon am Ende der Klassendefinition nicht weggelassen werden darf .
Der Inhalt im Klassenkörper wird 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 .

Zwei Möglichkeiten, Klassen zu definieren :

  1. Alle Deklarationen und Definitionen werden im Klassenkörper platziert. Beachten Sie, dass der Compiler eine Mitgliedsfunktion, die in der Klasse definiert ist , möglicherweise als Inline-Funktion behandelt .
    Fügen Sie hier eine Bildbeschreibung ein
  2. Die Klassendeklaration wird in der .h-Datei abgelegt, und die Definition der Mitgliedsfunktion wird in der .cpp-Datei abgelegt. Hinweis: Dem Namen der Mitgliedsfunktion muss ein vorangestellt werdenKlassenname::
    Fügen Sie hier eine Bildbeschreibung ein

4. Klassenzugriffsqualifikatoren und Kapselung

4.1 Zugangsqualifikationen

C++ kapselt normalerweise Objekte und Methoden zusammen und stellt durch selektive Zugriffsberechtigungen Schnittstellen für externe Benutzer bereit. Zugriffsberechtigungen werden in die folgenden drei Typen unterteilt: Fügen Sie hier eine Bildbeschreibung ein
[Beschreibung des Zugriffsqualifikators] :

  1. Auf durch public geänderte Mitglieder kann direkt außerhalb der Klasse zugegriffen werden, während durch protected und private geänderte Mitglieder das Gegenteil sind. (geschützt und privat sind hier ähnlich)
  2. Der Zugriffsbereich beginnt mit dem Auftreten dieses Zugriffsqualifikationsmerkmals und endet mit dem Auftreten des nächsten Zugriffsqualifikationsmerkmals. Wenn später kein Zugriffsqualifizierer vorhanden ist, endet der Bereich bei }, dem Ende der Klasse.
  3. Die Standardzugriffsrechte der Klasse sind privat und die Struktur ist öffentlich. (Weil struct mit C kompatibel sein muss)

Tipps: Zugriffsqualifizierer sind nur zur Kompilierungszeit nützlich. Wenn die Daten dem Speicher zugeordnet werden, gibt es keinen Unterschied bei den Zugriffsqualifizierern.

[ Klassische Interviewfrage ]: Was ist der Unterschied zwischen Struktur und Klasse in C++?
Antwort : C++ muss mit der C-Sprache kompatibel sein, damit Strukturen in C++ als Struktur verwendet werden können. Darüber hinaus kann struct in C++ auch zum Definieren von Klassen verwendet werden. Dies ist dasselbe wie das Definieren einer Klasse durch eine Klasse. Der Unterschied besteht darin, dass die Standardzugriffsberechtigung einer durch die Struktur definierten Klasse öffentlich und die Standardzugriffsberechtigung einer durch die Klasse definierten Klasse privat ist.


4.2 Verpackung

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 der Ein-/Ausschalter, 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.
Fügen Sie hier eine Bildbeschreibung ein
Computerbenutzer müssen sich jedoch 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 sie den Computer einschalten und wie man über Tastatur und Maus mit dem Computer interagiert. . 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 .


5. Umfang des Unterrichts

Eine Klasse definiert einen neuen Bereich , und alle Mitglieder der Klasse liegen innerhalb des Gültigkeitsbereichs der Klasse. Wenn Sie Mitglieder außerhalb einer Klasse definieren, müssen Sie den ::-Bereichsoperator verwenden, um anzugeben, zu welchem ​​Klassenbereich das Mitglied gehört. ( Eine Klasse unterscheidet sich von anderen Bereichen. Beim Kompilieren handelt es sich um ein Ganzes. Beim Definieren einer Variablen kann sie sich an einer beliebigen Stelle befinden. Der Compiler sucht global im Bereich der Klasse. )

[Code-Demonstration] :

class Date
{
    
    
public:
	void Print();

private:
	int _year;
	int _month;
	int _day;
};

//类体外定义成员函数
void Date::Print()
{
    
    
	cout << "_year" << "_month" << "_day" << endl;
}

6. Instanziierung von Klassen

Der Prozess der Erstellung eines Objekts aus einem Klassentyp wird als Instanziierung der Klasse bezeichnet.

Eine Klasse beschreibt ein Objekt . Sie ist so etwas wie ein Modell . Sie begrenzt die Mitglieder der Klasse. Durch das Definieren einer Klasse wird kein tatsächlicher Speicherplatz für deren Speicherung zugewiesen. Eine Klasse kann jedoch mehrere Objekte instanziieren, und die instanziierten Objekte belegen tatsächlich physischen Raum und speichern Klassenmitgliedsvariablen .

Lassen Sie uns eine Analogie verwenden. Das Instanziieren von Objekten aus einer Klasse ist wie die Verwendung architektonischer Entwurfszeichnungen, um ein Haus in der Realität zu bauen. Eine Klasse ist wie eine Entwurfszeichnung. Sie entwirft nur das, was benötigt wird, aber es gibt kein physisches Gebäude. Ebenso ist eine Klasse nur ein Entwurf instanziiert. Objekte können tatsächlich Daten speichern und physischen Raum belegen.
Fügen Sie hier eine Bildbeschreibung ein


7. Klassenobjektmodell

7.1 Speicherregeln für Klassenobjekte

Schauen wir uns zunächst diesen Code an, um die Größe von Klassen und Objekten zu ermitteln.

class A
{
    
    
public:
void PrintA()
{
    
    
   cout<<_a<<_i<<_d<<endl;
}

private:
char _a;
int _i;
double _d;
};

Eine Klasse kann sowohl Mitgliedsvariablen als auch Mitgliedsfunktionen haben. Lassen Sie mich zunächst sagen, dass die Berechnungsregeln für Mitgliedsvariablen und -strukturen dieselben sind , aber was ist mit Mitgliedsfunktionen?

Da die Mitgliedsvariablen in jedem Objekt unterschiedlich sind, aber dieselbe Funktion aufgerufen wird, wird beim Speichern als normale Funktion beim Erstellen mehrerer Objekte durch eine Klasse eine Kopie des Codes in jedem Objekt gespeichert, und zwar derselbe Code mehrfach gespeichert werden, was zu Platzverschwendung führt . Daher speichern wir in C++ nur Mitgliedsvariablen für Klassen, während Mitgliedsfunktionen im öffentlichen Codeabschnitt gespeichert werden .
Fügen Sie hier eine Bildbeschreibung ein

7.2 Beispiele

// 类中既有成员变量,又有成员函数
class A1 {
    
    
public:
    void f1(){
    
    }
private:
    int _a;
};

// 类中仅有成员函数
class A2 {
    
    
public:
   void f2() {
    
    }
};

// 类中什么都没有---空类
class A3
{
    
    };

Finden Sie: sizeof(A1) : ______ sizeof(A2) : ______ sizeof(A3) : ______ (Ergebnis: 4, 1, 1)

Analyse: Es besteht kein Zweifel, dass die Größe von A1 4 Byte beträgt. Allerdings zählt es nicht, in A2 nur eine Funktion im öffentlichen Codebereich zu haben, und A3 hat absolut keine Klasse. Für diesen Sonderfall definiert C++ seine Größe als 1 Byte (Platzhalter, speichert keine Daten, sondern zeigt nur an, dass das Objekt existiert hat

Zusammenfassung: Die Größe einer Klasse ist eigentlich die Summe der „Mitgliedsvariablen“ in der Klasse. Natürlich müssen Sie auf die Speicherausrichtung achten. Achten Sie auf die Größe der leeren Klasse. Die leere Klasse ist etwas Besonderes. Der Compiler gibt der leeren Klasse ein Byte, um das Objekt dieser Klasse eindeutig zu identifizieren. (Stellt einen Platzhalter dar, speichert keine Daten, sondern zeigt nur an, dass das Objekt vorhanden ist

7.3 Regeln zur Strukturspeicherausrichtung

  1. Das erste Mitglied befindet sich im Offset 0 von der Struktur.
  2. Andere Mitgliedsvariablen sollten an Adressen ausgerichtet werden, die ganzzahlige Vielfache einer bestimmten Zahl (Ausrichtungsnummer) sind.
    Hinweis: Ausrichtungsnummer = der kleinere Wert aus der Standardausrichtungsnummer des Compilers und der Größe des Elements.
    Die Standardanzahl der Ausrichtungen in VS beträgt 8
  3. Die Gesamtgröße der Struktur beträgt: ein ganzzahliges Vielfaches der maximalen Ausrichtungsnummer (der größte aller Variablentypen und der kleinste Standard-Ausrichtungsparameter).
  4. Wenn eine Struktur verschachtelt ist, wird die verschachtelte Struktur auf ein ganzzahliges Vielfaches ihrer eigenen maximalen Ausrichtungszahl ausgerichtet, und die Gesamtgröße der Struktur ist ein ganzzahliges Vielfaches aller maximalen Ausrichtungszahlen (einschließlich der Ausrichtungszahl verschachtelter Strukturen).

[Klassische Interviewfragen] :

  1. Wie richtet man Strukturen aus? Warum brauchen wir eine Gedächtnisausrichtung?
  2. Wie richtet man die Struktur gemäß den angegebenen Ausrichtungsparametern aus? Kann es nach 3, 4, 5, also jedem Byte, ausgerichtet werden?
  3. Was ist Big- und Small-Endianness? Wie kann man testen, ob eine bestimmte Maschine Big-Endian oder Little-Endian ist? Sind Sie jemals auf ein Szenario gestoßen, in dem Big- oder Small-Endian berücksichtigt werden muss?

8. Dieser Zeiger

Definieren wir zunächst eine Datumsklasse Date

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; 
};

int main()
{
    
    
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d2.Print();
	return 0;
}

Für die obige Klasse gibt es ein solches Problem:
Es gibt zwei Mitgliedsfunktionen, Init und Print, in der Date-Klasse. Es gibt keinen Unterschied zwischen verschiedenen Objekten im Funktionskörper. Wenn also d1 die Init-Funktion aufruft, wie verhält sich die Funktion? Wissen Sie, dass das d1-Objekt festgelegt werden soll? Anstatt das d2-Objekt festzulegen?

C++ löst dieses Problem durch die Einführung des Zeigers this, das heißt: Der C++-Compiler fügt jeder „nicht statischen Mitgliedsfunktion“ einen versteckten Zeigerparameter hinzu, sodass der Zeiger auf das aktuelle Objekt zeigt (das Objekt, das die Funktion aufruft, wenn die Funktion aktiviert ist). Der Zugriff auf alle Operationen an „Mitgliedsvariablen“ im Funktionskörper erfolgt über diesen Zeiger. Es ist nur so, dass alle Vorgänge für den Benutzer transparent sind, das heißt, der Benutzer muss sie nicht übergeben, der Compiler schließt sie automatisch ab.

8.2 Eigenschaften dieses Zeigers

  1. Der Typ dieses Zeigers: Der Typ der Klasse ist const*, d. h. in der Mitgliedsfunktion kann diesem Zeiger kein Wert zugewiesen werden.
    2.Dieser Zeiger kann nur innerhalb einer „Member-Funktion“ verwendet werden. (Dies kann nicht an den Positionen des Aktualparameters und des Formalparameters angezeigt und geschrieben werden, aber es kann in der Klasse angezeigt und verwendet werden.)
  2. Der This-Zeiger ist im Wesentlichen ein formaler Parameter einer „Member-Funktion“. Wenn ein Objekt eine Member-Funktion aufruft, wird die Objektadresse als tatsächlicher Parameter an diesen Parameter übergeben. Daher wird dieser Zeiger nicht im Objekt gespeichert.
  3. Dieser Zeiger ist der erste implizite Zeigerparameter der „Mitgliedsfunktion“. Im Allgemeinen wird er automatisch vom Compiler über das ECX-Register übergeben und muss nicht vom Benutzer übergeben werden.

Fügen Sie hier eine Bildbeschreibung ein

8.3 Klassische Interviewfragen

Interviewfragen (1)

[Interview Fragen]:

  1. Wo existiert dieser Zeiger?
  2. Kann dieser Zeiger null sein?

Antwort: Dieser Zeiger existiert im Stapelbereich. Es ist nicht möglich, diesem einfach eine Null zuzuweisen, aber es kann erzwungen werden, direkt eine Null zuzuweisen, aber eine solche Operation wird im Allgemeinen nicht ausgeführt.

Interviewfragen (2)

Werfen wir einen Blick auf diese Interviewfrage von Tencent:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
    
    
 void Print()
 {
    
    
 cout << "Print()" << endl;
 }
private:
 int _a;
};
int main()
{
    
    
 A* p = nullptr;
 p->Print();
 return 0;
}
//解析:本题主要在于P是否真的进行了空指针的解引用。
//     由于p指向的成员变量,但Print函数实际存在公共代码区(和全局变量类似)
//      所以实际上编译器是不会对p进行解引用。而是直接去符号表中查找Print函数地址。所以答案为C

// 2.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
    
     
public:
    void PrintA() 
   {
    
    
        cout<<_a<<endl;
   }
private:
 int _a;
};
int main()
{
    
    
    A* p = nullptr;
    p->PrintA();
    return 0;
}
//解析:这上面一样,编译器不会对p进行空指针的解引用,而是将p作为this指针传给PrintA函数。
//      而PrintA函数中,需要通过this(即p的形参)解引用指向_a,对空指针进行非法行为,运行崩溃

Nun, dieser Blog endet hier, ich hoffe, er kann Ihnen helfen.
Fügen Sie hier eine Bildbeschreibung ein
Fügen Sie hier eine Bildbeschreibung ein

Supongo que te gusta

Origin blog.csdn.net/Zhenyu_Coder/article/details/133362343
Recomendado
Clasificación