2010-11-24 8 views
1

Ich bin etwas neu in C++ also, ich denke, das ist eine sehr grundlegende Frage.Überladen = Operator in C++, wenn Arrays von Werten zu kopieren sind

Angenommen, ich habe diese Klasse:

// file Graph.h 
class Graph { 
public: 
    Graph(int N); // contructor 
    ~Graph();  // destructor 
    Graph& operator=(Graph other); 
private: 
    int * M; 
    int N; 
}; 

// file Graph.cpp 
Graph :: Graph(int size) { 
    M = new int [size]; 
    N = size; 
} 

Graph :: ~Graph() { 
    delete [] M; 
} 

Ich möchte einen Zuweisungsoperator erstellen, die der Inhalt von Array kopiert M [] aber , es nicht zu überschreiben, wenn ich es nach der Kopie ändern (Ich denke, das wird erreicht, indem man den eigentlichen Zeiger nicht kopiert, sondern nur den Inhalt, weiß nicht, ob ich recht habe). Das habe ich ausprobiert:

Graph& Graph::operator=(Graph other) { 
    int i; 
    N = other.N; 
    M = new int [N]; 
    for (i = 0; i < N; i++) 
    M[i] = other.M[i]; 
    return *this; 
} 

Ist das korrekt? Gibt es andere Möglichkeiten, dies zu tun?

edit: Eine wichtige Frage, die ich vergessen habe. Warum muss ich es wie Graph& operator=(Graph other); deklarieren und nicht nur: Graph operator=(Graph other); was ist in meinem Buch (C++: The Complete Reference, 2. Auflage, Herbert Schildt, Seiten 355-357) geschrieben?

+2

Vielleicht finden Sie [diese FAQ] (http://stackoverflow.com/questions/4172722/) interessant. Wenn Sie 'int *' durch 'std :: vector ' ersetzen, ersparen Sie sich eine Welt der Schmerzen. – fredoverflow

Antwort

9

Der kanonische Weg besteht darin, eine std::vector<int> zu verwenden, um die Speicherverwaltung selbst zu vermeiden. obwohl für die Ausübung der richtige Weg zu tun, was Sie wollen, ist:

#include <algorithm> 

class Graph 
{ 
public:  
    Graph(size_t n) { data_ = new int[n](); size_ = n; } 

    Graph(Graph const& g) 
    { 
     data_ = new int(g.size_); 
     size_ = g.size_; 
     std::copy(g.data_, g.data_ + g.size_, data_); 
    } 

    ~Graph() { delete[] data_; } 

    void swap(Graph& g) throw() 
    { 
     std::swap(data_, g.data_); 
     std::swap(size_, g.size_); 
    } 

    Graph& operator=(Graph g) { g.swap(*this); return *this; } 

private: 
    int* data_; 
    size_t size_; 
}; 

Google „Kopie und Swap-Idiom“ für die Logik hinter dem Code. Beachten Sie, dass Ihr Zuweisungsoperator Speicher verliert (das ursprüngliche Array wird überschrieben, aber nie gelöscht). Wenn die Zuweisung fehlschlägt, liegt ein fehlerhaftes Objekt vor. Darüber hinaus wird x = x nicht tun, was es zu tun ist. Diese drei Fallstricke sind üblich, und Schreibzuweisungsoperatoren im Copy-and-Swap-Stil vermeiden sie.

BEARBEITEN: Für Ihre andere Frage ermöglicht die Rückgabe einer Referenz die Zuordnung von Zuordnungen, wie a = b = c, die für integrierte Typen gültig ist. Es kann oder kann nicht sein, was Sie wollen (ist es normalerweise).

+0

ist "Kopieren und Swap-Idiom" (was ich nicht kenne) ein Grund für die Übergabe des Parameters an den Auftrag op by value und nicht durch Bezugnahme? – davka

+2

@davka: ja. Der Zuweisungsoperator "kopiert und tauscht mit * diesem" sein Argument. Die Kopie wird erstellt, wenn der Wert übergeben wird, und der Standard ermöglicht es dem Compiler, diese Kopie zu vermeiden, wenn dies nicht notwendig ist. –

+0

+1 für den C++ - Weg vorschlagen, 'vector' - ich würde diesen Teil tatsächlich mehr betonen. –

0

könnten Sie auch std :: mehr hier kopieren std::copy

oder können Sie memcpy Arrays sowie

1

Sie würden wahrscheinlich wollen einen Kopierkonstruktor erklären und zu definieren.

In der Tat ist es ein Muss in Ihrer Situation, weil der Standard-Kopierkonstruktor bei der Zerstörung zu einem doppelten delete führen wird.

Ich denke, es ist idiomatischer, den Kopierkonstruktor im Zuweisungsoperator zu verwenden (ein Kopieraufbau gefolgt von einem Austausch).

Wie für den aktuellen Code gibt es ein Speicherleck (weil das alte M ist nicht delete d).

+0

sollte ich ein 'delete M' vor' M = new int [N]; '? –

+0

@Rafael: Sie können, aber es hat ein anderes Problem. Wenn die Zuweisung fehlschlägt (durch Auslösen einer Ausnahme) und Sie diesen Fehler feststellen, hat das Objekt, dem Sie zuweisen, seinen ursprünglichen Inhalt verloren. Sie müssen also eine temporäre Variable verwenden. –

+0

ganz zu schweigen von den Gefahren der Selbstzuteilung, wie von Chowlett angegeben. – lijie

0

Vergessen Sie nicht, dass es zulässig ist zu schreiben graph_a = graph_a; Ihr Code wird den Speicher verlieren, der ursprünglich für graph_a.M reserviert wurde, und Sie werden mit nicht initialisiertem Speicher in M nach der Kopie enden.

Bevor Sie eine Kopie-Zuordnung zu tun, müssen Sie prüfen, ob Sie das gleiche Objekt über sich selbst nicht zu kopieren (in dem Fall, dass Sie nur zurückgeben kann).

3

Sie benötigen die & in operator=, so dass es keine Kopie verursacht, wenn Sie *this zurückgeben. Die wichtigere Frage ist, warum Sie etwas zurückgeben müssen. Die Antwort ist a=b=c Syntax zu unterstützen.

Ich würde vorschlagen, Sie Memcpy für Standard-Arrays von Einbau-int-ähnlichen Typen (oder Zeiger). Der Standard definiert es so, dass der Compiler-Writer eine Möglichkeit hat, die schnellstmögliche plattformspezifische Implementierung bereitzustellen.

Verwenden Memcpy nicht, wenn der Typ ein Objekt (wird nicht Copykonstruktor anrufen und viele andere schlechte Dinge)

+0

+1 für die Erklärung von memcpy – Francesco

+1

verwendet 'std :: copy' nicht die effizienteste Methode zum Kopieren von Objekten (was' memcpy' für dumme Objekte sein kann)? Ich würde denken, dass das in C++ eher idiomatisch wäre, als mit "memcpy" herumzuspielen. – lijie

+0

@lijie - Ich bin mir nicht sicher, aber vielleicht. –

0

Sie einen Verweis auf das Objekt zurückgeben müssen, die Sie aufgebaut haben. Wenn Sie eine Kopie zurückgegeben, würden Sie dann weiter kopieren ein anderes Objekt zu konstruieren, die ein Verwerfen, die Sie bereits haben.

1

fast alle haben gesagt worden, aber es gibt ein paar wichtige Hinweise:

  • ist es ratsam, die Parameter zu übergeben Konstruktor und Zuweisungsoperator als const Referenz, dh const Graph& other, kopieren schwere Kopieren zu vermeiden in ein temporären Objekt (es sei denn Du ein "copy and swap" idiom
  • , wenn man sie privat Zeiger als Klasse Mitglied Sie muss (na ja, solltest in den meisten Fällen) bietet sowohl Kopie ctor und Zuweisungsoperator, oder deaktivieren, indem sie verwendet verwenden Ansonsten werden die Standardeinstellungen verwendet ein Leck verursachen.
  • schließlich, warum nicht std::vector verwenden? Sie sparen all diese Probleme ohne spürbare Leistungseinbußen.

kann diese Seite hilfreich sein - einfach und umfassend: C++ Operator Overloading Guidelines

+0

Ich denke, es gibt einen Fall für die Übergabe von Wert an den Zuweisungsoperator (der Körper des Zuweisungsoperators ist dann nur ein Tausch von * diesem und dem Parameter). – lijie

+0

@lijie: Ja, ich habe es gerade aus Alexandres Antwort gelernt. Aber man sollte das komplette Idiom verwenden, um es zu einem gültigen Fall zu machen. – davka

+0

Daher sollte der erste Aufzählungspunkt geändert werden? Es sieht aus wie eine Decke "nein", um den Parameter als Wert an den Zuweisungsoperator zu übergeben. – lijie

1

haben einige davon bereits von anderen gesagt worden, aber wenn wir mit den grundlegenden Layout bleiben möchten, dass Sie haben, sollten Sie dies ändern:

  • Fabrikat „Graph andere“ konstante -> „const Graph andere“ (Gutes zu tun, so dass Sie ‚versehentlich‘ ändern dont die Daten in „anderen“)
  • Haben Sie einen Scheck auf „andere“ zu Beachten Sie, dass es sich nicht um das gleiche Objekt wie das Objekt handelt, dem Sie zuweisen (der L-Wert). Kann dies tun, wenn Aussage ein einfaches, indem sie -> if (dies == & andere)
  • den Speicher M. Löschen (Wir wollen don Speicherlecks :))

btw Oh. müssen Sie nicht auf C-Syntax von "i" in der Start der Funktion erklärt wird -> "for (int i = ...."

Hope it :) half