2016-09-07 6 views
10

Dies ist wahrscheinlich eine einfache Frage, aber ich habe diese template class:unsichere Vorlage Array Konstruktor

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

Der Code ist von einem Kurs pdf-Datei, wo es heißt buff(new Type[n]) unsicher ist. Ich verstehe nicht, warum es unsicher ist, ist size_t im Allgemeinen nicht signiert? Kann ich ein Beispiel haben, wo es einen Kompilierungs- und/oder Laufzeitfehler haben könnte?

+1

Gut für den Anfang haben Sie keinen Destruktor, und das könnte zu einem Speicherverlust führen –

+0

@ArnavBorborah aber ist das das einzige * unsichere * Ding? – Loay

Antwort

12

Der Code ist "unsicher" in der Tatsache, dass es n vor buff gebaut wird. Diese Abhängigkeit fügt dem Code Brüchigkeit hinzu.

Wenn Sie die Mitglieder der Klasse bauen sie in der Reihenfolge aufgebaut sind, die in der Klasse deklariert sind, nicht, wie sie in der Initialisierungsliste genannt, so dass, wenn der Code

template<typename Type> 
class Array { 
    Type* buff; 
    size_t n; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

geändert wurde Dann, wenn Sie buff(new Type[n]) tun, ist n nicht initialisiert und Sie haben undefiniertes Verhalten.

+1

Beachten Sie, dass sowohl [gcc und clang] (http://coliru.stacked-crooked.com/a/08d42bdb5e31d8ed) Sie warnen, wenn Sie die Mitglieder in einer Member-Initialisiererliste in einer anderen Reihenfolge als sie erstellt werden und Sie Habe -Wall (oder genauer gesagt, Wreorder) aktiviert. – jaggedSpire

+0

@jaggedSpire, und das ist die Warnung, die mich am meisten nervt! – SergeyA

+0

@jaggedSpire Das ist nett, ich wusste nicht, dass es das getan hat. Zu schlecht benutzen zu viele Leute das nicht. – NathanOliver

-1

Es ist nicht sicher, einen neuen Operator in der Initialisierungsliste aufzurufen. Wenn new fehlschlägt, wird der Destruktor von Array nicht aufgerufen. Hier ist eine ähnliche Frage. Are there any issues with allocating memory within constructor initialization lists?

+0

Das gilt hier nicht, obwohl es wäre, wenn es einen Nicht-'noexcept'-Code gibt, der nach dem' neuen' ausgeführt wird, sogar ein anderes 'neues' new schlägt fehl, dann schlägt die Konstruktion fehl, nichts anderes in der Klasse wird dynamisch zugewiesen, also räumt es nach sich selbst. – jaggedSpire

3

Zunächst wird den Auftrag, die Initialisierungen für den Konstruktor ausgeführt werden, durch die Reihenfolge nicht bestimmt wird sie aufgeschrieben wird, sondern durch die Reihenfolge der initialisierten Felder im Code angezeigt:

class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

Hier erstes n wird initialisiert und dann buff.

Jetzt wird der erste Buff initialisiert und dann n, also hat n in diesem Fall keinen definierten Wert.

Die Verwendung von Initialisierungslisten für Konstruktoren ist eine gute Vorgehensweise, aber seien Sie vorsichtig, dass Sie keine Annahmen in der Reihenfolge erstellen.

Im Allgemeinen ist es eine gute Idee, auf rohe Zeiger zu verzichten. Wenn Sie stattdessen Smartpointer verwenden, können Sie nicht vergessen, die Daten freizugeben.

Im konkreten Fall möchten Sie vielleicht auch std :: vector anstelle eines C-artigen Arrays verwenden. Damit werden alle Zuordnungen, Umlegungen, Freigaben etc. für Sie threadsicher abgewickelt. Es scheint, als ob Sie versuchen, etwas wie Ihren eigenen std :: vector zu schreiben. Bitte tun Sie das nur für Bildungszwecke. Immer die Standardimplementierung im Produktionscode bevorzugen. Sie werden es wahrscheinlich eine ganze Weile nicht besser bekommen. Wenn ja, würden Sie hier verschiedene Fragen stellen ;-)

+0

Dieser Beispielcode ist für Bildungszwecke gedacht – Loay

3

Zunächst einmal haben Sie ein Speicherleck. Aber die Frage ist wahrscheinlich nicht so. Nehmen wir an, Sie haben einen Destruktor, der das Array freigibt.

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
    ~Array() { delete[] buff; } 
}; 

Jetzt dieser bestimmte Code ist absolut sicher. Beim Zuweisen von n_ kann keine Ausnahme ausgelöst werden, die Reihenfolge der Initialisierung ist korrekt und buff ist der einzige rohe Zeiger in Ihrer Klasse. Wenn Sie jedoch beginnen, Ihre Klasse zu erweitern und weitere Klassen zu schreiben, erhöht sich das Risiko eines Speicherlecks.

Stellen Sie sich vor, dass Sie eine weitere Mitglieder in die class Array hinzufügen müssen:

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
    SomethingElse xyz; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {} 
    ~Array() { delete[] buff; } 
}; 

Wenn der Konstruktor von SomethingElse wirft, der Speicher für buff zugeordnet wird auslaufen, weil die destructor ~Array() nie aufgerufen werden.

Moderne C++ ruft Zeiger wie Type* buffrohe Zeiger, weil Sie sind verantwortlich für das Aufheben von Zuweisungen Lagerung selbst (Ausnahmen zu berücksichtigen) und stellen Tools wie std::unique_ptr und std::shared_ptr, die Pflege von Speicher Deallokation automatisch erfolgen kann.

In der modernen C++ Sie Ihre Klasse wie folgt schreiben könnte:

template<typename Type> 
class Array { 
    size_t n; 
    std::unique_ptr<Type[]> buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n_]) {} 
}; 

Hinweis das Fehlen eines destructor. Die unique_ptr kümmert sich um den Anruf delete für Sie.

Beachten Sie auch, keine Abhängigkeit von den Teilnehmern in der Initialisierungsliste (einfach new Type[n_] statt new Type[n] Schreiben macht den Code robuster)

0

C++ 98 Standard 12.6.2.5.4 (Ich erwarte nicht, neue Versionen das entspannt haben).

- Dann werden nicht statische Datenelemente in der Reihenfolge initialisiert werden sie in der Klassendefinition deklariert wurden (wieder unabhängig von der Reihenfolge des mem-initializers).

So ist die Reihenfolge der Initialisierung entsprechend definiert.

Wenn Sie ein Beispiel für den Absturz haben möchten, machen Sie einfach sizeof(Type)*n> Gesamtspeicher in Ihrem System.