C ++ Konstruktor mit Zeigern kopieren

8

Kann jemand die Bedeutung von *p=*q in diesem C ++ Code erklären? Ist das ein Kopierkonstruktorkonzept?

%Vor%     
RakshaGShenoy 04.09.2016, 13:06
quelle

3 Antworten

13
  

Ist das ein Kopierkonstruktorkonzept?

Nein, worauf Sie sich beziehen, ist ein Kopierzuweisungskonzept. Bedenken Sie Folgendes:

%Vor%

Wie Sie oben sehen können, wird nur der Wert der Variable q , auf die verwiesen wird, kopiert. Dies entspricht der Kopierzuordnung für Objekte. Wenn Sie das tun würden:

%Vor%

Dann wäre das keine Kopie-Zuweisung, weil beide int auf dieselbe Adresse und denselben Wert zeigen, was bedeutet, dass jede Änderung in p oder q auf die andere Variable reflektiert wird. Um ein konkreteres und validiertes Beispiel zu geben, hier ist ein Code:

%Vor%

Und hier ist ein ergänzendes Gegenbeispiel

%Vor%

Wie Sie sehen, spiegeln sich die Änderungen in beiden Variablen für p=q

wider

Seitenhinweis Sie haben das Kopieren erwähnt, aber Sie waren sich nicht sicher über das Konzept. So hätte die Kopierkonstruktion ausgesehen:

%Vor%

Die Kopierkonstruktion unterscheidet sich von der Kopierzuweisung in dem Sinn, dass die Variable bei der Konstruktion der Kopie nicht bereits einen Wert hat und der Konstruktor für ein Objekt noch aufgerufen werden muss. Das Mischen der 2 Begriffe ist üblich, aber die fundamentalen Unterschiede machen die beiden Konzepte, naja, anders.

    
Arnav Borborah 04.09.2016 13:15
quelle
6

Es sieht so aus, als ob Sie den Kopierkonstruktor und die Kopierzuweisung nicht kennen. Lassen Sie uns zuerst beide Konzepte einzeln betrachten, und dann komme ich zu Ihrer Frage. Die Antwort ist ein wenig lang, also sei geduldig:)

Konstruktor kopieren

Hier werde ich nicht erklären, wie man einen Kopierkonstruktor schreibt, sondern wann der Kopierkonstruktor aufgerufen wird und wann nicht. (Wenn Sie wissen möchten, wie Sie einen Kopierkonstruktor erstellen, lesen Sie dies )

Ein Kopierkonstruktor ist ein spezieller Konstruktor zum Erstellen eines neuen Objekts als Kopie eines vorhandenen Objekts. (Sie wird immer dann aufgerufen, wenn eine Kopie eines vorhandenen Objekts erstellt werden muss)

In diesen Szenarien wird der Kopierkonstruktor aufgerufen, um die Kopie eines vorhandenen Objekts zu erstellen:

  • Initialisieren eines Objekts mit einem zuvor erstellten Objekt :

    %Vor%

    Siehe, SomeClass obj; Anweisung erstellt einfach ein Objekt (hier wird der Standardkonstruktor aufgerufen um das Objekt zu erstellen). Die zweite Anweisung SomeClass anotherObj = obj; instanziiert ein Objekt, das mit den Werten von obj (ein existierendes Objekt) initialisiert wurde, so dass der Kopierkonstruktor hier aufgerufen wird. Sie können ein Objekt auch mit einem vorhandenen Objekt auf diese Weise initialisieren: SomeClass anotherObj(obj); (Diese Anweisung entspricht SomeClass anotherObj = obj; )

    Außer :
    Wenn Sie mit einem -Wertausdruck initialisieren. z.B.

    %Vor%

    In diesem Fall wird der move -Konstruktor aufgerufen. Siehe Was sind Bewegungssemantiken?

  • Übergeben Sie ein Objekt nach Wert an eine Funktion (siehe Argumente nach Wert übergeben ):

    Siehe das folgende Code-Snippet, hier akzeptiert die Funktion doSomething ein Objekt als Parameter nach Wert:

    %Vor%

    Es gibt einige Fälle, in denen die Kopie des übergebenen Arguments im Parameterobjekt someObject erstellt werden muss. Ich habe aufgeführt, wann die Kopie erstellt werden muss und wann nicht ein Bedürfnis sein.

    Sehen Sie sich das folgende Code-Snippet an:

    %Vor%

    Die Anweisung SomeClass someObject; instanziiert nur someObject durch Aufruf des Standardkonstruktors .

    Die zweite Anweisung doSomething(someObject); ruft die zuvor angezeigte Funktion doSomething auf, indem someObject als Argument übergeben wird. Dies ist der Fall, wenn eine Kopie von someObject erstellt werden muss, um sie an die Funktion zu übergeben.

    Außer :
    Similiary, wenn wir doSomething mit einem rvalue-Ausdruck aufrufen, wird move-Konstruktor anstatt Kopierkonstruktor aufgerufen.

  • Rückgabe eines Objekts aus einer Funktion nach Wert :

    Sehen wir uns die folgende Definition von doSomething

    an %Vor%

    In der obigen Funktion doSomething wird ein Objekt von SomeClass erstellt und nachdem eine Aufgabe ausgeführt wurde, wird das Objekt von der Funktion zurückgegeben. In diesem Fall wird eine Kopie von someObject erstellt und zurückgegeben.

    Außer :
    Similiary, Wenn doSomething einen rvalue-Ausdruck zurückgibt, wird move-Konstruktor statt Kopierkonstruktor aufgerufen.

Zuordnung kopieren

Die Kopierzuweisung wird normalerweise mit der Kopierkonstruktion verwechselt. Schauen wir uns an, wie sie sich von der Kopierkonstruktion unterscheidet:

%Vor%

Die ersten beiden Anweisungen erstellen nur someObject und anotherObject , die dritte Anweisung ruft tatsächlich den Kopierzuweisungsoperator und nicht den Kopierkonstruktor auf.

Konstruktoren werden nur aufgerufen, wenn ein neues Objekt erstellt wird. Und im Falle von anotherObject = someObject; sind beide Objekte bereits erstellt, so dass kein Aufruf des Kopierkonstruktors erfolgt. Stattdessen wird ein Kopierzuweisungsoperator aufgerufen (um zu erfahren, wie man einen Kopierzuweisungsoperator überlädt, siehe dies ) )

Sehen wir uns nun Ihr Code-Snippet an:

%Vor%

In der ersten Anweisung A *p=new A(); wird der Standardkonstruktor aufgerufen, um ein Objekt zu erstellen (in diesem Fall wird ein neues Objekt auf dem Heap erstellt) und p wird mit der Adresse des neu erstellten Objekts initialisiert (z p ist ein Zeiger )

Ähnlich verhält es sich mit der zweiten Anweisung A *q=new A(); (Es wird ein anderes Objekt erstellt und q wird mit dem neu erstellten Objekt initialisiert)

Nun, die dritte Aussage: *p = *q; (hier ist * Dereferenzierungsoperator )

Um zu verstehen, was die dritte Anweisung macht, schauen wir uns einige Zeiger an und de-referenzieren sie, um das tatsächliche Objekt zu erhalten, auf das sie zeigen.

%Vor%

Versuchen wir, das obige Code-Snippet zu verstehen: someVariable wird erstellt und mit einem Wert von 5 initialisiert, dann wird somePointer erstellt und mit der Adresse von someVariable initialisiert.

Nun, die letzte Anweisung *somePointer = 7; , es wird tatsächlich die somePointer de-referenziert und durch De-Referenzierung erhält es die Variable, auf die es zeigt. (so wird es someVariable erhalten) und dann wird 7 zugewiesen. Nach dieser Aussage wird someVariable zu 7

Nehmen wir ein anderes Beispiel:

%Vor%

Zuerst wird somePointer erstellt und initialisiert mit der Adresse der dynamisch zugewiesenen int Variable (wird im Heap zugewiesen, siehe Dynamische Zuweisung in c ++ ), wird anotherPointer ebenfalls mit der Adresse einer anderen dynamisch zugewiesenen Variablen initialisiert.

Dann wird 5 der ersten Variablen zugewiesen (auf die von somePointer verwiesen wird) und 7 auf die zweite Variable (auf die anotherPointer zeigt)

Nun wird die letzte Anweisung von Interesse sein, *somePointer = *anotherPointer; , in dieser Anweisung wird *somePointer denerfiziert und erhält die erste Variable (deren Wert 5 war) und *anotherPointer ist referenzierend und Abrufen der zweiten Variablen (deren Wert 7 ist) und Zuweisen der zweiten Variablen an die erste Variable, was dazu führt, dass der Wert der ersten Variablen in 7 geändert wird.

Sehen wir uns nun Ihre *p=*q; -Anweisung an, ( p zeigte auf das erste Objekt von A und q zeigte auf das zweite Objekt von A , beide dynamisch zugewiesen) , *p wird dereferenziert p und erhält das erste Objekt, *q wird deneferenziert q und erhält das zweite Objekt, und dann wird das zweite Objekt in das erste Objekt kopiert, und wenn Sie kein neues Objekt sehen wird in *p=*q; erstellt, und nur der Wert des zweiten Objekts wird im ersten Objekt erstellt, daher wird Kopierzuweisungsoperator hier aufgerufen und kein Kopierkonstruktor.

Eine andere Sache

Sie sollten den dynamisch zugewiesenen Speicher, den Sie ausgeliehen haben, mit new operator:

entziehen %Vor%

Sie sollten diese beiden Zeilen am Ende Ihres Programms hinzufügen, um Memory Leak zu vermeiden.

    
Muhammad Ahmad 04.09.2016 13:50
quelle
4

Wie in den Kommentaren erwähnt, muss man zuerst die Grundlagen verstehen:

  • Mit A *p=new A(); erhält man einen Zeiger auf einen Speicherbereich auf dem Heap, in dem ein Objekt vom Typ A konstruiert ist.

  • Mit *p dereferenziert man den Zeiger, d. h. man ruft das Objekt ab.

  • Jetzt verwendet *p = *q den (möglicherweise implizit deklarierten) Zuweisungsoperator der Klasse A , um *p anzugeben - das Objekt, auf das p zeigt - den Wert von *q . Diese Operation könnte äquivalent als p->operator=(*q) geschrieben werden.

Der letzte Schritt, die Zuweisung, ist identisch mit dem, was Sie mit Objekten anstelle von Zeigern erhalten würden (was normalerweise eine bessere Methode ist, in C ++ zu gehen und oft als RAII bezeichnet wird):

%Vor%     
davidhigh 04.09.2016 15:59
quelle

Tags und Links