2016-07-13 33 views
5

Ich bin ein Java-Entwickler versucht, C++ abzuholen. Ist es in Ordnung, einen Setter in einem Konstruktor zu verwenden, um die vom Setter bereitgestellten Plausibilitätsprüfungen wieder zu verwenden?Verwenden von Setter in Konstruktor

Zum Beispiel:

#include <stdexcept> 
using namespace std; 

class Test { 
    private: 
     int foo; 
     void setFoo(int foo) { 
      if (foo < 42) { 
       throw invalid_argument{"Foo < 42."}; 
      } 

      this->foo = foo; 
     } 

    public: 
     Test(int foo) { 
      setFoo(foo); 
     }; 
}; 
+0

Es ist gültig. Beachten Sie, dass 'unsigned' Typen existieren, um den Test hier los zu werden. – Jarod42

+0

Wenn Sie nicht möchten, dass Benutzer der Klasse keine negativen Zahlen übergeben, verwenden Sie stattdessen 'unsigned'. Dann wird der Compiler die Überprüfung zum Zeitpunkt der Kompilierung für Sie übernehmen, anstatt dass Sie eine Laufzeitprüfung benötigen. –

+2

Das 'unsigned' Zeug ist nicht unumstritten; siehe z.B. https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50, 42:40, 1:02:50 (Panel, in dem mehrere prominente Mitglieder des Komitees diskutieren gegen die Verwendung von vorzeichenlosen Ganzzahlen für etwas anderes als Bit-Fiddling.) –

Antwort

5

Ja, es wird empfohlen, dies zu tun, grundsätzlich aus dem bereits erwähnten Grund.

Auf der anderen Seite sollten Sie sich fragen, ob Sie den Setter überhaupt brauchen und die Checks im Konstruktor nicht direkt implementieren. Der Grund, warum ich dies schreibe, ist, dass Setter im Allgemeinen einen veränderbaren Zustand ergeben, der viele Nachteile gegenüber unveränderlichen Klassen hat. Manchmal sind sie jedoch erforderlich.

Eine weitere Empfehlung: Wenn Ihre Klasse Variable ein Objekt ist, und Sie können den Konstruktor dieses Objekts ändern, könnten Sie den Scheck in den Konstruktor dieses Objekt setzen:

class MyFoo { 
public: 
    MyFoo(int value) { 
     if (value < 42) { 
      throw invalid_argument{"Foo < 42."}; 
     } 
     v = value; 
    } 
private: 
    int v; 
} 

Dies ermöglicht es Ihnen, eine verwenden, Initialisierungsliste im Konstruktor Ihrer Test Klasse:

Test(int foo) : foo(foo) {} 

jedoch jetzt die Prüfung ist eine Eigenschaft der Klasse der variablen und nicht mehr eine der besitzenden Klasse.

1

Ja, das ist in Ordnung, solange es Sinn, einen Setter für eine bestimmte Elementvariable haben macht (ein gewisse Logik hat, die nicht durch Zuordnung überprüft werden kann nur zum Beispiel). In diesem Beispiel könnte setFoo einfach eine unsigned int genommen haben und der Anrufer würde wissen, keine negativen Werte zu übergeben. Was wiederum den Check und damit die Notwendigkeit eines Setter eliminieren könnte. Für ausführlichere Prüfungen ist ein Setter und die Verwendung dieses Setter im Konstruktor in Ordnung.

3

Ja, Sie können. Es ist in Ordnung, solange Ihre Setter nicht virtual sind, weil es Vererbungshierarchie in rufenden Funktionen ist, da das "diese" ptr noch nicht bereit ist.

ist hier Herb Sutter GOTW zu diesem Thema: http://www.gotw.ca/gotw/066.htm

+0

@ user2079303 Ich stimme zu, es ist in Ordnung, solange dev weiß, welche Funktion in einer solchen Situation aufgerufen wird. Ich habe nie über Fehler nachgedacht, die vom Aufruf von nicht-virtuellen Funktionen kommen, die virtuelle onces aufrufen. Guter Punkt. – paweldac

+0

@ user2079303: _ "Was Sie absolut nicht tun sollten, ist eine Mitgliedsfunktion aufzurufen, die ihrerseits ein virtuelles Mitglied aufruft" _ Warum nicht? Wie ist das anders als das, was du gerade gesagt hast, sicher? –

+0

@ user2079303: 'Fraid so. Nicht-Konstruktoren führen eine dynamische Verteilung aus und Konstruktoren führen eine dynamische Verteilung aus. Das Ergebnis ist deterministisch. Es ist möglicherweise nicht das, was Sie erwartet haben, wenn Sie gerade dabei sind, ein abgeleiteteres Objekt zu konstruieren, aber [es ist "sicher" und hat sicherlich in beiden Fällen den gleichen Effekt] (http://coliru.stacked-crooked.com/a/9a0f6498a5df9acb). –

1

Kurze Antwort: Ja. In der Tat funktioniert Ihr Beispiel.

Lange Antwort: Aber es ist keine gute Praxis. Zumindest müssen Sie vorsichtig sein.

Im Allgemeinen funktioniert eine Set-Funktion mit einem konstruierten Objekt. Es wird angenommen, dass die Invariante der Klasse gilt. Die Funktionen in einer Klasse werden implementiert, wobei die Invariante als wahr betrachtet wird.

Wenn Sie möchten, dass andere Funktionen in einem Konstruktor verwendet werden, müssten Sie Code schreiben. Zum Beispiel, um ein leeres Objekt zu erstellen.

Zum Beispiel, wenn Sie in Ihrer Klasse ändern SetFoo in der Zukunft (sagen wir SetFoo ändert das Mitglied foo nur, es ist größer) Sie Beispiel zu arbeiten aufhören.

0

Ich weiß, das passt nicht zu Ihrer Situation. Es ist nur der Vollständigkeit halber:

Wenn Sie einfach nur Member-Werte (ohne Checks wie Ihre in setFoo) festlegen, ist es empfehlenswert, Initialisierungslisten im Konstruktor zu verwenden. Dies verhindert, dass Mitglieder 2 Mal "initialisiert" werden: 1. mit ihrem Standardwert, 2. mit dem Wert, den Sie an den Konstruktor übergeben haben.

class Test { 
private: 
    int foo_; 

public: 
    Test(int foo) 
     : foo_(foo) 
    { }; 
};