C++ Computer Advanced Programming Language

Grundlegende Inhalte

Der Unterschied zwischen prozessorientiert und objektorientiert※※

Verknüpfung

1. Unterschied

  1. Verschiedene Programmierideen
    Prozessorientiert: Es handelt sich um eine prozesszentrierte Programmieridee. Beide sind auf das Geschehen als Hauptziel programmiert.
    Objektorientiert: Es handelt sich um eine Art Programmiersprache, die Objekte als grundlegende Programmstruktureinheit verwendet. Dies bedeutet, dass das zur Beschreibung verwendete Design auf Objekten basiert und Objekte die Grundkomponenten der Programmlaufzeit sind.

  2. Verschiedene Funktionen
    Prozessorientiert: Es geht darum, die zur Lösung des Problems erforderlichen Schritte zu analysieren, diese Schritte dann mithilfe von Funktionen Schritt für Schritt zu realisieren und sie bei ihrer Verwendung einzeln aufzurufen.
    Objektorientiert: Es geht darum, das konstitutive Problem in verschiedene Objekte zu zerlegen. Der Zweck der Einrichtung eines Objekts besteht nicht darin, einen Schritt abzuschließen, sondern das Verhalten von etwas im gesamten Problemlösungsschritt zu beschreiben.

  3. Verschiedene Vorteile
    Prozessorientiert: Unterstützt keine umfangreichen „objektorientierten“ Funktionen (wie Vererbung, Polymorphismus) und erlaubt keine Vermischung von persistentem Zustand und Domänenlogik.
    Objektorientiert: Wird intern als Zeiger auf eine Reihe von Eigenschaften dargestellt. Jede Operation an diesem Objekt durchläuft die Eigenschaften und Methoden dieses Zeigeroperationsobjekts.

Zweitens die Vor- und Nachteile

Prozessorientierte Programmierung (prozedurorientierte Programmierung)
ist spezifisch und rationalisiert. Um ein Problem zu lösen, müssen Sie es Schritt für Schritt analysieren und Schritt für Schritt realisieren.
Vorteile: Die Leistung ist besser als bei objektorientierten, da die Klasse beim Aufruf instanziiert werden muss, der Overhead relativ groß ist und Ressourcen verbraucht. Beispielsweise übernehmen Einzelchip-Mikrocomputer, eingebettete Entwicklung, Linux/Unix usw. im Allgemeinen eine prozessorientierte Entwicklung, und die Leistung ist der wichtigste Faktor.
Nachteile: nicht leicht zu warten, nicht leicht wiederzuverwenden, nicht leicht zu erweitern.

Objektorientierte Programmierung (objektorientierte Programmierung)
Die objektorientierte Programmierung ist modelliert. Sie müssen lediglich eine Klasse abstrahieren, bei der es sich um eine geschlossene Box handelt, in der Sie über Daten und Methoden zur Lösung von Problemen verfügen. Sie können alle Funktionen, die Sie benötigen, direkt verwenden und müssen sie nicht Schritt für Schritt implementieren. Was interessiert uns, wie diese Funktion implementiert wird? Wir werden es nutzen.
Vorteile: einfache Wartung, einfache Wiederverwendung und einfache Erweiterung. Aufgrund der Eigenschaften der objektorientierten Kapselung, Vererbung und Polymorphie kann ein System mit geringer Kopplung entworfen werden, um das System flexibler und einfacher zu warten. Wartbarkeit manifestiert sich in drei Aspekten: Verständlichkeit. Testbarkeit und Modifizierbarkeit.
Nachteile: Die Leistung ist geringer als bei prozessorientiert.

3. Zusammenfassung

  1. Objektorientiert ist ein hohes Maß an physikalischer Abstraktion und prozessorientiert ist Top-Down-Programmierung!
  2. Objektorientiert bedeutet, das Problem nach Funktion und nicht nach Schritten zu unterteilen.
  3. Die unterste Ebene der Objektorientierung ist eigentlich prozessorientiert. Die Prozessorientierung wird in Klassen abstrahiert und dann gekapselt, sodass wir objektorientiert verwenden können.

Der Unterschied zwischen C und C++

Links
1. Unterschiedliche Strukturen

  1. C-Sprache: Die C-Sprachstruktur hat nur Mitgliedsvariablen, aber keine Mitgliedsmethoden.
  2. C++: Eine C++-Struktur kann über eigene Mitgliedsvariablen und Mitgliedsfunktionen verfügen.

2. Verschiedene Designs

  1. C-Sprache: Die C-Sprache wird für die prozedurale und abstrakte allgemeine Programmierung verwendet.
  2. C++: C++ kann nicht nur prozedurale Programmierung in der C-Sprache durchführen, sondern auch objektbasierte Programmierung, die durch abstrakte Datentypen gekennzeichnet ist, und objektorientierte Programmierung, die durch Vererbung und Polymorphismus gekennzeichnet ist.

3. Verschiedene Funktionsbibliotheken

  1. C-Sprache: Die C-Sprache verfügt über eine Standardfunktionsbibliothek, die lose ist. Fügen Sie einfach Funktionen mit derselben Funktion in eine Header-Datei ein.
  2. C++: C++ ist für die meisten Funktionen sehr eng integriert und ein Kollektiv.

C++-Datentypen

  1. Grundtyp
    1. ganze Zahl
      1. kurze Int
      2. ganze Zahl (int)
      3. lange Ganzzahl (long)
    2. Zeichentyp (char)
    3. Gleitkomma
      1. Typ mit einfacher Genauigkeit (Float)
      2. Doppelter Typ (doppelt)
      3. langes Doppel
    4. Boolescher Wert (bool)
  2. abgeleiteter Typ
    1. Zeigertyp (*)
    2. Aufzählungstyp (enum)
    3. Array-Typ ([ ])
    4. Strukturtyp (struct)
    5. Gewerkschaftstyp (Union)
    6. Klassentyp (Klasse)
  3. leerer Typ (void)

Der Unterschied zwischen ++i und i++

Verknüpfung
++i ist effizienter, ++i generiert während des Vorgangs keine temporären Objekte, was zurückgegeben wird, ist i, was ein L-Wert ist, Ausdrücke wie ++i=1 sind zulässig,Und i++ generiert während des Vorgangs temporäre Objekte, gibt den Wert des Objekts zum Zeitpunkt Null zurück, was ein R-Wert ist, und Ausdrücke wie i++=1 sind unzulässig.
Bei integrierten Typen werden die separaten i++- und ++i-Anweisungen grundsätzlich vom aktuellen Compiler in ++i optimiert, sodass es keinen Unterschied gibt

Der Link
++i fügt zuerst eins zu i hinzu und gibt dann den Wert von i zurück, während i++ zuerst den Wert von i zurückgibt und dann eins zu i hinzufügt.

Gibt es einen Unterschied zwischen globalen Variablen und lokalen Variablen im Speicher? Wenn ja, was ist der Unterschied?

Verknüpfung

  1. Der Lebenszyklus ist anders.
    1. Globale Variablen werden im statischen Speicherbereich gespeichert. Globale Variablen und statische Variablen werden zusammen gespeichert. Initialisierte globale Variablen und statische Variablen befinden sich im selben Bereich, und nicht initialisierte globale Variablen und statische Variablen werden in einem angrenzenden Bereich gespeichert. Dieser Bereich wird nach Programmende vom System freigegeben.
    2. Lokale Variablen werden auf dem Stapel gespeichert. Wird vom Compiler automatisch zugewiesen und freigegeben und speichert Funktionsparameterwerte, lokale Variablenwerte usw. Es funktioniert wie ein Stapel in einer Datenstruktur
  2. Der Handlungsspielraum ist unterschiedlich.
    1. Globale Variablen haben einen globalen Gültigkeitsbereich. Globale Variablen müssen nur in einer Quelldatei definiert werden und können auf alle Quelldateien angewendet werden. Natürlich müssen andere Quelldateien, die keine globalen Variablendefinitionen enthalten, diese globale Variable erneut mit dem Schlüsselwort extern deklarieren.
    2. Lokale Variablen haben auch nur einen lokalen Gültigkeitsbereich. Es handelt sich um ein automatisches Objekt (Auto). Es existiert nicht ständig während der Ausführung des Programms, sondern nur während der Ausführung der Funktion. Nachdem ein Aufruf der Funktion ausgeführt wurde , wird die Variable widerrufen und der belegte Speicher ebenfalls zurückgefordert
  3. Statische Variablen werden in statische externe Variablen und statische lokale Variablen unterteilt, die alle zur Kompilierungszeit Speicher zuweisen

Die Aufteilung des Speicherplatzes für Benutzer im Arbeitsspeicher

Tan Haoqiang P109-
Link

  1. Programmbereich
  2. Statischer Speicherbereich (Speicher: globale Variablen, statische Variablen)
  3. Dynamischer Speicherbereich (Speicher: formale Parameter der Funktion, in der Funktion definierte Variablen (lokale Variablen ohne statische Deklaration), Feldschutz und Rücksprungadresse beim Aufruf der Funktion usw.)
  4. Der Stapel ist der Speicherbereich für Variablen, die vom Compiler bei Bedarf zugewiesen und automatisch gelöscht werden, wenn sie nicht benötigt werden. Die darin enthaltenen Variablen sind normalerweise lokale Variablen, Funktionsparameter usw. In einem Prozess befindet sich an der Spitze des virtuellen Adressraums des Benutzers der Benutzerstapel, der vom Compiler zum Implementieren von Funktionsaufrufen verwendet wird.
  5. Der Heap besteht aus den von new zugewiesenen Speicherblöcken. Ihr Release-Compiler kümmert sich nicht um sie und wird von unserer Anwendung gesteuert. Im Allgemeinen entspricht ein new einem Löschvorgang. Wenn der Programmierer es nicht freigibt, wird das Betriebssystem es nach Programmende automatisch wiederverwenden. Der Heap kann dynamisch erweitert und wiederhergestellt werden.

Die Form der formalen Parameterübergabe

Tan Haoqiang P184-
Link

  1. Wert übergeben
  2. Zeigerübergabe
  3. als Referenz übergeben

Funktion

Erklären Sie den Unterschied zwischen Funktionsprototypdeklaration und Funktionsdefinition.

Die Deklaration des Funktionsprototyps erklärt lediglich, wie die Funktion verwendet werden soll, welche Daten ihr beim Aufruf der Funktion übergeben werden sollen und wie das Ergebnis des Funktionsaufrufs verwendet werden soll.
Zusätzlich zu den Nutzungsinformationen der Funktion muss die Funktionsdefinition auch den vollständigen Prozess angeben, wie die Funktion die erwartete Funktion erreicht, d. h. wie die Ausgabe aus der Eingabe erhalten wird.

Im Funktionsprototyp kann der Parametername geschrieben werden oder nicht, das Kompilierungssystem überprüft den Parameternamen jedoch nicht. Es spielt also keine Rolle, wie der Parametername lautet.

float add(float, float);
float add(float x, float y);

überlastete Funktion

überlastete FunktionAnzahl der Parameter, Parametertyp oder ParameterreihenfolgeMindestens einer der drei muss unterschiedlich sein und die Rückgabewerttypen der Funktion können gleich oder unterschiedlich sein.

Was sind Funktionsvorlagen? Was sind Template-Funktionen? Wofür werden Funktionsvorlagen verwendet?

Die Funktionsvorlage (Funktionsvorlage) dient eigentlich zum Erstellen einer allgemeinen Funktion. Der Funktionstyp (Rückgabewerttyp) und der formale Parametertyp werden nicht angegeben, sondern durch einen virtuellen Typ dargestellt. Diese generische Funktion wird zu einer Funktionsvorlage.

① Eine Funktionsvorlage bedeutet, dass der Typ eines Parameters oder Rückgabewerts in einer Funktion unsicher und variabel ist. Diese unsicheren Typen werden als Vorlagenparameter bezeichnet.
② Wenn für den Vorlagenparameter der Funktionsvorlage ein bestimmter Typ angegeben wird, wird eine ausführbare Funktion erhalten, die als Vorlagenfunktion bezeichnet wird.
③ Funktionsvorlagen können Programmierern die Arbeit ersparen. Mehrere Funktionen mit unterschiedlichen zu verarbeitenden Datentypen, aber mit genau demselben Verarbeitungsablauf können als Funktionsvorlage geschrieben werden.

③ Wenn der Funktionskörper gleich ist, ist die Anzahl der Parameter der Funktion gleich, aber die Typen sind unterschiedlich, die Funktionsvorlage ist praktischer als die Funktionsüberladung und das Programm ist prägnanter.

template <typename T>
T max(T a, T b)
{
    
    
	XXX
}
template <class T>
T max(T a, T b)
{
    
    
	XXX
}

Zeiger

Was ist der Unterschied zwischen einer Referenz und einem Zeiger?※※

Verknüpfung

  1. Ein Zeiger zeigt auf einen Speicherblock und sein Inhalt ist die Adresse des Speichers, auf den verwiesen wird; eine Referenz ist ein Alias ​​für einen Speicherblock.
  2. Referenzen müssen bei Verwendung nicht dereferenziert werden (* Zeigeroperator oder indirekter Zugriffsoperator), Zeiger müssen dereferenziert werden.
  3. Referenzen können zum Definitionszeitpunkt nur einmal initialisiert werden und sind danach unveränderlich; Zeiger sind veränderbar.
  4. Referenzen haben keine Konstante, Zeiger haben Konstanten und Konstantenzeiger sind unveränderlich.
  5. Referenzen können nicht null sein, Zeiger können null sein;
  6. „sizeof reference“ ruft die Größe der Variablen (des Objekts), auf die verwiesen wird, ab, während „sizeof pointer“ die Größe des Zeigers selbst (die Adresse der Variablen oder des Objekts, auf die verwiesen wird) erhält. ) immer wahr, immer wahr, aber wenn eine Referenz typeid(T) == typeid(T&vorhanden sizeof(T) == sizeof(T&)ist Wenn es als Mitglied verwendet wird, ist sein Footprint derselbe wie der eines Zeigers (ich habe den Standard nicht gefunden).
  7. Was die Post-Inkrementierungsoperation (i++) betrifft, spiegelt die Operation an der Referenz direkt das Objekt wider, auf das gezeigt wird, anstatt die Ausrichtung zu ändern; während die Operation am Zeiger den Zeiger auf das nächste Objekt zeigt, anstatt die Ausrichtung zu ändern Objekt. Inhalt

Zeiger auf const-Objekt und const-Zeiger auf Objekt

Tan Haoqiang P177-
Link

Konstanter Zeiger auf das Objekt

Die allgemeine Form zum Definieren eines konstanten Zeigers auf ein Objekt ist
类名 * const 指针变量名 = 对象地址;

Deklarieren Sie die Zeigervariable, die auf das Objekt zeigt, als const-Typ und initialisieren Sie sie.Der Zeigerwert behält immer seinen Anfangswert und kann nicht geändert werden, das heißt, sein Zeiger kann nicht geändert werden, aber der Wert des Datenelements (nicht konstanter Typ) in dem Objekt, auf das er zeigt, kann geändert werden

Time t1(10,12,15), t2;
Time * const ptr1 = &t1;
ptr1 = &t2;

Hinweis: Zeigervariablen sollten bei der Definition initialisiert werden.

Zeiger auf konstantes Objekt

Definiert das Format einer Zeigervariablen, die auf ein konstantes Objekt zeigt
const 类名 * 指针变量名;
类名 const * 指针变量名;

Wenn eine Variable als konstante Variable deklariert wurde, kann nur eine Zeigervariable, die auf eine konstante Variable zeigt, auf sie zeigen, und keine allgemeine Zeigervariable (kein konstanter Typ) kann darauf zeigen.Es ist nicht erlaubt, den Wert des Objekts, auf das es zeigt, über die Zeigervariable zu ändern, aber der Wert der Zeigervariablen p (dh der Punkt von p) kann geändert werden.
Ein Zeiger auf ein konstantes Objekt wird am häufigsten als formaler Parameter einer Funktion verwendet, um zu verhindern, dass sich Änderungen im Wert des Objekts, auf das der formale Zeigerparameter zeigt, auf den tatsächlichen Parameter auswirken.

const int a;//定义常变量a
const int *p;//定义指向常变量的指针
p = &a;

Das Gleiche gilt für Objekte. Wenn Sie ein Objekt als konstantes Objekt deklarieren, können Sie nur mit einem Zeiger auf ein konstantes Objekt darauf zeigen und können keine allgemeine (nicht konstante) Zeigervariable verwenden, um darauf zu zeigen.

const Time t1;//定义常对象
const Time *p;//定义指向常对象的指针
p=t1;

Klassen und Objekte

Konstrukteur

Tan Haoqiang P246

Einführung

C ++ bietet einen Konstruktor (Konstruktor) für die Objektinitialisierung . Ein Konstruktor ist eine spezielle Mitgliedsfunktion . Im Gegensatz zu anderen Mitgliedsfunktionen muss er nicht vom Benutzer aufgerufen werden, sondern wird beim Erstellen des Objekts automatisch ausgeführt .
Der Name des Konstruktors muss mit dem Namen der Klasse identisch sein und darf nicht beliebig benannt werden, damit das Kompilierungssystem ihn erkennen und als Konstruktor behandeln kann. Es hat keinen Typ und gibt keinen Wert zurück.
Wenn ein Objekt erstellt wird, weist das System dem Objekt eine Speichereinheit zu und der Konstruktor wird zu diesem Zeitpunkt ausgeführt.

Beachten Sie, dass
ein Klassenobjekt zum Initialisieren eines anderen Klassenobjekts verwendet werden kann, z. B.:

Time t1;			// 建立对象t1,同时调用构造函数 t1.Time()
Time t2 = t1;	//建立对象t2,并用一个 t1 初始化 t2

Kopieren Sie zu diesem Zeitpunkt den Wert jedes Datenelements von Objekt t1 in jedes entsprechende Element von t2, ohne den Konstruktor t2.Time() aufzurufen.

Initialisieren Sie Datenelemente mit der Parameterinitialisierungstabelle

Box::Box(int h, int w, int len): height(h), width(w), length(len)
{
    
    
	XXX
}

Wenn das Datenelement ein Array ist, sollte ihm eine Anweisung im Funktionskörper des Konstruktors zugewiesen werden und kann nicht in der Parameterinitialisierungstabelle initialisiert werden.

Konstruktorüberladung

Das Kompilierungssystem bestimmt, welcher Konstruktor der Form des Funktionsaufrufs entspricht.
Es wird ein Konstruktor aufgerufen, der beim Erstellen eines Objekts keine tatsächlichen Parameter angeben mussStandardkonstruktor. Eine Klasse kann nur einen Standardkonstruktor haben.

Wenn beim Erstellen eines Objekts ein parameterloser Konstruktor ausgewählt wird, gilt Folgendes:

Box box1;	//建立对象的正确形式
Box box2();	//建立对象的错误形式,不应该有括号

Im Programm sollte kein parameterloser Konstruktor (wie z. B. Box()) aufgerufen werden. Hinweis: Der Konstruktor kann vom Benutzer nicht explizit aufgerufen werden.

Obwohl in einer Klasse mehrere Konstruktoren enthalten sein können, wird für jedes Objekt beim Erstellen des Objekts nur einer der Konstruktoren ausgeführt, nicht jeder Konstruktor wird ausgeführt.

Konstruktor mit Standardparametern

class Box
{
    
    
public:
	Box(int h = 10, int w = 10, int len = 10);	//在声明构造函数时指定默认参数
};

Bei der Deklaration eines Konstruktors kann der Parametername weggelassen werden:

class Box
{
    
    
public:
	Box(int = 10, int = 10, int = 10);	//在声明构造函数时指定默认参数
};

Wenn alle Parameter des Konstruktors Standardwerte angeben, können bei der Definition des Objekts ein oder mehrere Aktualparameter angegeben werden oder keine Aktualparameter angegeben werden.Da ein Konstruktor ohne tatsächliche Parameter aufgerufen werden kann, ist ein Konstruktor, bei dem für alle Parameter Standardwerte angegeben sind, auch ein Standardkonstruktor. Eine Klasse kann nur einen Standardkonstruktor haben, dh einen Konstruktor, der ohne Parameter aufgerufen werden kann, und eine Klasse kann nur einen haben.
Es liegt ein Fehler vor, wenn die folgenden zwei Konstruktoren gleichzeitig definiert werden:

Box();
Box(int = 10, int = 10, int = 10);

Nachdem Sie einen Konstruktor mit allen Standardparametern in einer Klasse definiert haben, können Sie keinen überladenen Konstruktor definieren.(Es wird Unklarheiten geben)

Klassifizierung von Konstruktoren in C++

(1) Standardkonstruktor.
Am Beispiel der Student-Klasse lautet der Prototyp des Standardkonstruktors
Student(); // keine Parameter
(2) Initialisierungskonstruktor
Student(int num, int age); // Parameter
(3) Kopieren (Kopieren) Konstruktor
Student( Student&);//Der formale Parameter ist ein Verweis auf dieses Klassenobjekt
(4) Konvertierungskonstruktor
Student(int r);//Der formale Parameter besteht aus anderen Variablentypen und es gibt nur einen formalen Parameter

Bitte erläutern Sie die Rolle von Destruktor und Konstruktor

  1. Der Konstruktor wird beim Erstellen eines neuen Objekts automatisch ausgeführt und dient der Objektinitialisierung. Der Destruktor wird automatisch ausgeführt, wenn das Objekt zerstört wird, und erledigt einige Nacharbeiten.
  2. Der Name des Konstruktors ist der Klassenname und der Name des Destruktors ist die Tilde plus der Klassenname.
  3. Weder der Konstruktor noch der Destruktor müssen den Rückgabetyp der Funktion schreiben.
  4. Objekte können unterschiedliche Konstruktoren haben, sodass eine Klasse eine Reihe überladener Konstruktoren, aber nur einen Destruktor haben kann. Konstruktoren können auch eine Initialisierungsliste haben.

Kopierkonstruktor (Compy-Konstruktor)

Tan Haoqiang P277

Die Rolle des Kopierkonstruktors besteht darin, die Mitgliedswerte des tatsächlichen Parameterobjekts nacheinander den entsprechenden Mitgliedern im neuen Objekt zuzuweisen.
Es wird allgemein vereinbart, eine const-Deklaration hinzuzufügen, damit die Parameter nicht geändert werden können, um das tatsächliche Parameterobjekt nicht aufgrund von Unachtsamkeit beim Aufrufen dieser Funktion zu ändern.

Verknüpfung

Der Kopierkonstruktor ist eine Art Konstruktor, der auch als Kopierkonstruktor bezeichnet wird. Er hat nur einen Parameter und der Parametertyp ist ein Verweis auf diese Klasse.
Die Parameter des Kopierkonstruktors können konstante oder nicht konstante Referenzen sein. Ersteres wird im Allgemeinen verwendet, sodass konstante Objekte (Objekte, deren Wert nach der Initialisierung nicht geändert werden kann) als Parameter und nicht konstante Objekte als Parameter zum Initialisieren anderer Objekte verwendet werden können. Es ist auch möglich, zwei Kopierkonstruktoren in eine Klasse zu schreiben, wobei ein Parameter eine konstante Referenz und der andere Parameter eine nicht konstante Referenz ist.
Wenn der Designer der Klasse keinen Kopierkonstruktor schreibt, generiert der Compiler automatisch einen Kopierkonstruktor.In den meisten Fällen besteht seine Funktion darin, ein byteweises Kopieren vom Quellobjekt zum Zielobjekt zu realisieren, dh jede Mitgliedsvariable des Zielobjekts gleich dem Quellobjekt zu machen. Der vom Compiler automatisch generierte Kopierkonstruktor wird als „Standardkopiekonstruktor“ bezeichnet.
Notiz,Der Standardkonstruktor (d. h. der Konstruktor ohne Argumente) existiert nicht unbedingt, aber der Kopierkonstruktor existiert immer.

Standard-Kopierkonstruktor:

#include<iostream >
using namespace std;
class Complex
{
    
    
public:
    double real, imag;
    Complex(double r, double i) {
    
    
        real= r; imag = i;
    }
};
int main(){
    
    
    Complex cl(1, 2);
    Complex c2 (cl);  //用复制构造函数初始化c2
    cout<<c2.real<<","<<c2.imag;  //输出 1,2
    return 0;
}

Benutzerdefinierter Kopierkonstruktor:

#include<iostream>
using namespace std;
class Complex{
    
    
public:
    double real, imag;
    Complex(double r,double i){
    
    
        real = r; imag = i;
    }
    Complex(const Complex & c){
    
    
        real = c.real; imag = c.imag;
        cout<<"Copy Constructor called"<<endl ;
    }
};

int main(){
    
    
    Complex cl(1, 2);
    Complex c2 (cl);  //调用复制构造函数
    cout<<c2.real<<","<<c2.imag;
    return 0;
}

Verwendung des Kopierkonstruktors (wann wird der Kopierkonstruktor verwendet)

  1. Das Programm muss ein neues Objekt erstellen und es mit einem anderen Objekt derselben Art initialisieren.
  2. Wenn der Parameter der Funktion ein Objekt der Klasse ist.Beim Aufruf einer Funktion muss das tatsächliche Parameterobjekt vollständig an den formalen Parameter übergeben werden, dh es muss eine Kopie des tatsächlichen Parameters erstellt werden, d. h. ein formaler Parameter wird entsprechend dem tatsächlichen Parameter kopiert. Das System erkennt dies durch Aufrufen des Kopierkonstruktors, sodass formale Parameter genau die gleichen Werte wie tatsächliche Parameter haben.
  3. Der Rückgabewert der Funktion ist ein Objekt der Klasse.Wenn der Funktionsaufruf abgeschlossen ist, wird der Rückgabewert an den Ort des Funktionsaufrufs zurückgegeben.

Wann ist es notwendig, einen eigenen Kopierkonstruktor einer Klasse zu definieren?

Wenn das Datenelement der Klasse einen Zeiger enthält und der Zeiger auf eine dynamische Variable zeigt, müssen Sie den Kopierkonstruktor selbst definieren. Wenn es andere spezielle Anforderungen an die Kopierkonstruktion gibt, müssen Sie auch einen Kopierkonstruktor definieren

Verknüpfung
Jede Klasse, die dynamisch zugewiesene Mitglieder oder Zeigermitglieder enthält, sollte einen Kopierkonstruktor bereitstellen

Link
Wenn Ihre Klasse eine tiefe Kopie benötigt, müssen Sie den Kopierkonstruktor anpassen, was auch einige Unfälle verhindern kann. Im Allgemeinen ist es am besten, einen Kopierkonstruktor anzupassen und eine tiefe Kopie zu implementieren. Da der vom Programm automatisch generierte Kopierkonstruktor eine flache Kopie ist, verweist das temporäre Objekt auf denselben Speicher wie das Originalobjekt. Wenn das temporäre Objekt außerhalb des Gültigkeitsbereichs freigegeben wird, wird auch der Speicherplatz des Originalobjekts freigegeben Gleichzeitig treten Probleme beim erneuten Verweisen auf das Originalobjekt auf, was ein sehr häufiges Problem ist, das durch flaches Kopieren verursacht wird.

flache Kopie und tiefe Kopie※※

Verknüpfung

  1. Für den Fall, dass der explizite Kopierkonstruktor nicht definiert ist, ruft das System die Standardkopierfunktion „Shallow Copy“ auf, mit der das Kopieren der Mitglieder einzeln abgeschlossen werden kann. Wenn das Datenelement keinen Zeiger enthält, ist eine flache Kopie möglich. Wenn jedoch ein Zeiger im Datenelement vorhanden ist und eine einfache flache Kopie verwendet wird, zeigen die beiden Zeiger in den beiden Klassen auf dieselbe Adresse. Wenn die Das Objekt endet bald. Der Destruktor wird zweimal aufgerufen, wodurch der Zeiger hängen bleibt. Daher muss zu diesem Zeitpunkt eine tiefe Kopie verwendet werden.
  2. Der Unterschied zwischen Deep Copy und Shallow Copy besteht darin, dass Deep Copy zusätzlichen Platz im Heap-Speicher zum Speichern von Daten beansprucht und so das Problem des hängenden Zeigers löst. Kurz gesagt: Wenn die Datenelemente Zeiger enthalten, muss eine tiefe Kopie verwendet werden.

Tomomoto

Freundfunktion

Deklarieren Sie gewöhnliche Funktionen als Friend-Funktionen

Wenn eine Funktion an einer anderen Stelle als dieser Klasse definiert ist (diese Funktion kann eine Nicht-Mitgliedsfunktion sein, die keiner Klasse angehört, oder eine Mitgliedsfunktion anderer Klassen), deklarieren Sie sie mit „friend“ im Klassenkörper, und diese Funktion wird dies tun Wird als Friend-Funktion dieser Klasse bezeichnet. Friend-Funktionen können auf private Mitglieder dieser Klasse zugreifen.

class Time
{
    
    
public:
	friend void display(Time &);
private:
	int hour,
	int minute;
	int sec;
};
void display(Time &)
{
    
    
	cout << t.hour << ":" << t.minute << ":" << t.sec << endl;
}

Freund-Mitgliedsfunktion

Hier wird die erweiterte

#include<iostream>
using namespace std;
class Date;				//对 Date 类的提前引用声明
class Time
{
    
    
public:
	void display(Date &);
};
class Date
{
    
    
public:
	friend void Time::display(Date &);		//声明Time 中的 display 函数为本类的友元成员函数
};
void Time::display(Date Td)
{
    
    
	cout << d.month << endl;			// 引用Date类对象中的私有数据
	cout << hour << endl;					// 引用本类对象中的私有数据
}

Freundesklasse

Wenn Klasse B eine Freundklasse von Klasse A ist, sind alle Funktionen in Freundklasse B Freundfunktionen von Klasse A und können auf alle Mitglieder von Klasse A zugreifen.

Verwenden Sie im Definitionshauptteil der Klasse A die folgende Anweisung, um Klasse B als ihre Freundklasse zu deklarieren:
friend B;

Notiz:

  1. Freundschaftsbeziehungen sind eher einseitig als zweiseitig.
  2. Freundschaftsbeziehungen sind nicht transitiv.

Klassenvorlagen und ihre Implementierungen

Tan Haoqiang P290

Wenn zwei oder mehr Klassen vorhanden sind und ihre Funktionen gleich sind und nur die Datentypen unterschiedlich sind, kann eine Klassenvorlage verwendet werden, um den Programmentwurf zu vereinfachen.
Da Klassenvorlagen Typparameter enthalten, werden sie auch parametrisierte Klassen genannt.
Eine Klassenvorlage ist eine Abstraktion einer Klasse und eine Klasse ist eine Instanz einer Klassenvorlage.

template<class T>
class Compare
{
    
    
public:
	Compare(Ta, T b)
	{
    
    
		x = a;
		y = b;
	}
	T max();
private:
	T x, y;
};

template<class T>
T Compare<T> :: max()
{
    
    
	return x > y ? x : y;
}

Vererbung und Ableitung

Ausführungsreihenfolge von Konstruktoren und Destruktoren von Basis- und abgeleiteten Klassen

Tan Haoqiang P350

Beim Erstellen eines Objekts lautet die Reihenfolge der Ausführung des Konstruktors: Der Konstruktor der abgeleiteten Klasse ruft zuerst den Konstruktor der Basisklasse auf und führt dann den Konstruktor der abgeleiteten Klasse selbst aus (dh den Funktionskörper des Konstruktors der abgeleiteten Klasse).
Wenn das abgeleitete Klassenobjekt freigegeben wird, wird zuerst der abgeleitete Klassendestruktor und dann sein Basisklassendestruktor ausgeführt.

Wenn die abgeleitete Klasse Unterobjekte (Unterobjekte, Objekte in Objekten) hat, ist die Reihenfolge, in der abgeleitete Klassenkonstruktoren ausgeführt werden, wie folgt:

  1. Rufen Sie den Basisklassenkonstruktor auf, um die Datenelemente der Basisklasse zu initialisieren
  2. Rufen Sie den Unterobjektkonstruktor auf, um die Datenelemente des Unterobjekts zu initialisieren
  3. Führen Sie dann den Konstruktor der abgeleiteten Klasse selbst aus, um die Datenelemente der abgeleiteten Klasse zu initialisieren

Mehrfachvererbung

Mit C++ kann eine abgeleitete Klasse mehrere Basisklassen gleichzeitig erben. Dieses Verhalten wird als Mehrfachvererbung bezeichnet.

class D: public A, private B, protected C
{
    
    
	xxx
};

Die Aufrufreihenfolge der Konstruktoren im obigen Beispiel: ABC, D

Polymorphismus und virtuelle Funktionen

Polymorphismus und wie er implementiert wird

Tan Haoqiang P379

Einführung

Polymorphismus ist ein wichtiges Merkmal der objektorientierten Programmierung. Wenn eine Sprache nur Klassen, aber keinen Polymorphismus unterstützt, kann sie nicht als objektorientierte Sprache bezeichnet werden, sondern nur als objektbasiert.
Senden Sie dieselbe Nachricht an verschiedene Objekte, und verschiedene Objekte weisen beim Empfang unterschiedliche Verhaltensweisen (dh Methoden) auf.
In C++ besteht eine der Erscheinungsformen des Polymorphismus darin, dass Funktionen mit unterschiedlichen Funktionen denselben Funktionsnamen verwenden können, sodass Funktionen mit unterschiedlichem Inhalt mit einem Funktionsnamen aufgerufen werden können.

Einstufung

statischer Polymorphismus
passierenFunktionsüberlastungerreichen. Der durch Funktionsüberladung und Operatorüberladung gebildete Polymorphismus (Operatorüberladung ist im Wesentlichen eine Funktionsüberladung) ist ein statischer Polymorphismus, der erfordert, dass beim Kompilieren des Programms alle Informationen über die aufrufende Funktion bekannt sind. Daher kann das System entscheiden, welche Funktion aufgerufen werden soll. statischer Polymorphismus, auch bekannt alsPolymorphismus zur Kompilierzeit.
Vor- und Nachteile: Funktionsaufrufe sind schnell und effizient, aber nicht flexibel, und die auszuführenden Funktionen und Methoden werden vor der Programmausführung festgelegt.
dynamischer Polymorphismus
passierenvirtuelle Funktion(virtuelle Funktion) Implementierung. Das Zielobjekt der Operation wird während der Ausführung des Programms dynamisch bestimmt, auch bekannt alsLaufzeitpolymorphismus

Warum einen virtuellen Destruktor definieren?

Durch Definieren des Destruktors als virtuelle Funktion können Speicherlecks verhindert werden.

Verknüpfung
Wenn der Destruktor der Basisklasse kein virtueller Destruktor ist, wird beim Zerstören des abgeleiteten Klassenobjekts nur der Destruktor der Basisklasse aufgerufen, und das abgeleitete Klassenobjekt kann nicht freigegeben werden, was zu einem Speicherverlust führt. Wenn der Destruktor der Basisklasse als virtueller Destruktor definiert ist, wird der Destruktor der abgeleiteten Klasse automatisch zu einer virtuellen Funktion. Beim Zerstören des abgeleiteten Klassenobjekts wird zuerst der Destruktor der abgeleiteten Klasse und dann die abgeleitete Klasse aufgerufen Die Klasse der Basisklasse wird aufgerufen. Funktion, sodass sowohl das übergeordnete Klassenobjekt als auch das untergeordnete Klassenobjekt vollständig freigegeben werden.

Was ist eine abstrakte Klasse? Was bringt es, eine abstrakte Klasse zu definieren? Welche Einschränkungen gibt es bei der Verwendung abstrakter Klassen?

Eine Klasse, die nicht zum Definieren eines Objekts, sondern nur als Basistyp für die Vererbung verwendet wird, wird als abstrakte Klasse (abstrakte Klasse) bezeichnet. Da sie häufig als Basisklasse verwendet wird, wird sie normalerweise als abstrakte Basisklasse (abstrakt) bezeichnet Basisklasse).
Eine Klasse, die rein virtuelle Funktionen enthält, wird als abstrakte Klasse bezeichnet. Da rein virtuelle Funktionen nicht aufgerufen werden können, können Klassen, die rein virtuelle Funktionen enthalten, keine Objekte erstellen.
Der Hauptzweck der Definition einer abstrakten Klasse besteht darin, das Verhalten der von dieser abstrakten Klasse abgeleiteten Klassen zu regulieren.
Bei der Verwendung können keine Objekte abstrakter Klassen definiert werden, sondern nur Zeigervariablen abstrakter Klassen.

Überlastung des Bedieners

Operatoren, die nicht überladen werden können

(1). (Mitgliedszugangsbetreiber)

(2).* (Mitgliedszeiger-Zugriffsoperator)

(3) ∷ (Feldoperator)

(4) sizeof (Längenoperator)

(5) ?: (bedingter Operator)

Die Bedeutung der Operatorüberlastung

Tan Haoqiang P301

Durch die Kombination von Operatorüberladung mit Klassen können neue Datentypen definiert werden, die in C++-Programmen sehr praktisch und bequem zu verwenden sind. Durch die Überladung von Operatoren wird C++ skalierbarer und anpassungsfähiger. Dies ist eine der leistungsstärksten und attraktivsten Funktionen von C++.

Matrixaddition

#include <iostream>
using namespace std;
class Matrix                                          //定义Matrix类
{
    
    
public:
	Matrix();                                          //默认构造函数
	friend Matrix operator+(Matrix&, Matrix&);        //重载运算符“+”
	void input();                                      //输入数据函数
	void display();                                    //输出数据函数
private:
	int mat[2][3];
};

Matrix::Matrix()                                      //定义构造函数
{
    
    
	for (int i = 0; i < 2; i++)
		for (int j = 0; j < 3; j++)
			mat[i][j] = 0;
}

Matrix operator+(Matrix& a, Matrix& b)                //定义重载运算符“+”函数
{
    
    
	Matrix c;
	for (int i = 0; i < 2; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			c.mat[i][j] = a.mat[i][j] + b.mat[i][j];
		}
	}
	return c;
}
void Matrix::input()                                   //定义输入数据函数
{
    
    
	cout << "input value of matrix:" << endl;
	for (int i = 0; i < 2; i++)
		for (int j = 0; j < 3; j++)
			cin >> mat[i][j];
}

void Matrix::display()                                //定义输出数据函数
{
    
    
	for (int i = 0; i < 2; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			cout << mat[i][j] << " ";
		}
		cout << endl;
	}
}

int main()
{
    
    
	Matrix a, b, c;
	a.input();
	b.input();
	cout << endl << "Matrix a:" << endl;
	a.display();
	cout << endl << "Matrix b:" << endl;
	b.display();
	c = a + b;                                         //用重载运算符“+”实现两个矩阵相加
	cout << endl << "Matrix c = Matrix a + Matrix b :" << endl;
	c.display();
	return 0;
}

I/O-Stream

Was ist der Unterschied zwischen einer ASCII-Datei und einer Binärdatei?

Eine ASCII-Datei interpretiert jedes in der Datei gespeicherte Byte als ASCII-Zeichen, und
eine Binärdatei interpretiert den Inhalt der Datei als binären Bitstrom, und das Programm interpretiert die Bedeutung dieser Bitströme.

おすすめ

転載: blog.csdn.net/qq_41286942/article/details/123767392