Frage zu Konstruktoren und Klassendesign
-
Hi,
ich schreibe gerade eine Klasse und weiss nicht recht wie ich folgendes am besten loesen soll.
class A { private: int _x; int _y; public: A(int x, int y) { if ( x > 255 ) _x = 255; else _x = x; if ( y > 255 ) _y = 255; else _y = y; } A(int xy) { // 1. nochmal auf x und y > 255 pruefen was redundant waere // 2. eine init methode schreiben die das macht // 3. den ersten Konstruktor hier aufrufen } };Diese Art von Problem kommt ja nun recht haeufig vor, also dass man Werte ueberpruefen muss und diese Werte einmal direkt dem Konstruktor zur Ueberpruefung gegeben werden und einmal dass die Werte indirekt von einem anderen Konstruktor erzeugt werden, aber auch geprueft werden soll.
Wie loest ihr sowas?
-
mit C++11 kannst du aus einem Konstruktor einen anderen Konstruktor aufrufen, das wäre hier sicherlich sinnvoll.
Allerdings frage ich mich, was genau du hiermit meinst:
A(int xy) { // 1. nochmal auf x und y > 255 pruefen was redundant waere // 2. eine init methode schreiben die das macht // 3. den ersten Konstruktor hier aufrufen }du übergibst doch nur ein int... sind die zwei Werte irgendwie in das eine int reincodiert?
-
Naja, wenn er meint, dass da nur Werte von 0 bis 255 drin stehen können, dann kann man die auch zu 4. in einen int reinschieben.
Aber ne Prüfung muss dann nicht mehr gemacht werden.
-
daddy_felix schrieb:
...
du übergibst doch nur ein int... sind die zwei Werte irgendwie in das eine int reincodiert?
Ich denke er will x und y auf den Wert von xy setzen.
Also in etwa so
class A { private: int _x; int _y; public: A(int x, int y) { if ( x > 255 ) _x = 255; else _x = x; if ( y > 255 ) _y = 255; else _y = y; } A(int xy) : A(xy, xy) { // 1. nochmal auf x und y > 255 pruefen was redundant waere // 2. eine init methode schreiben die das macht // 3. den ersten Konstruktor hier aufrufen } };
-
sollte das nicht eine Initializierung sein?
also etwa so?A(int x, int y) : _x (x > 255 ? 255 : x) , _y (y > 255 ? 255 : y) { }das "x > 255 ? 255 : x" koennte natürlich auch eine Function sein.
A(int x, int y) : _x (rangeCheckd(x)) , _y (rangeCheckd(y)) { }dann schaut es eventuell freundlicher aus
-
Ja, richtig, ich wollte in dem zweiten Konstruktor mit xy sowohl x als auch y initialisieren und natuerlich vorher wieder den Bereich pruefen. Aber es sollte nur ein Beispiel fuer die Problematik sein, mir ist da auf die schnelle nix besseres eingefallen.
Es geht also generell darum, Werte auf Gueltigkeit zu pruefen und verschiedene Konstruktoren zur Verfuegung zu stellen, die dann eventuell auch dieselben Tests durchlaufen muessen(Datei nicht gefunden, ausserhalb des Bereiches, ungueltige Daten, etc.).
-
kurze_frage schrieb:
das "x > 255 ? 255 : x" koennte natuerlich auch eine Function sein.
A(int x, int y) : _x (rangeCheckd(x)) , _y (rangeCheckd(y)) { }dann schaut es eventuell freundlicher aus
Ist das nicht ein Problem? Also, Methoden aufzurufen, obwohl das Objekt in der Initialisierungsliste noch nicht vollstaendig konstruiert wurde? Gibt das nicht eventuell undefiniertes Verhalten?
Wie loest ihr das denn immer? extra-init Funktion? Ueber das neue C11 Feature?
-
Hi,
Ich haette 3 Fragen.
1. Was sind x und y wo kommen die Werte her?
2. Kannst du die Daten nicht vorher validieren?
3. Du weisst das x und y bei dir theoretisch zwischen -2147483648 und 255 liegen koennen?
-
CppNeuland schrieb:
Ist das nicht ein Problem? Also, Methoden aufzurufen, obwohl das Objekt in der Initialisierungsliste noch nicht vollstaendig konstruiert wurde? Gibt das nicht eventuell undefiniertes Verhalten?
Nein, das ist ok.
Die rangeCheckd-Funktion nutzt ja nichtmal member-Variablen.(Vorsicht, Halbwissen):
Geht auch mit Memberfunktionen, die nur Variablen nutzen, die in der Klassendeklaration vor der zu initialisierenden Variablen stehen.
Da würde ich allerdings warten, bis das hier einer bestätigt (oder widerlegt).
-
Ruvi schrieb:
Hi,
Ich haette 3 Fragen.
1. Was sind x und y wo kommen die Werte her?
2. Kannst du die Daten nicht vorher validieren?
3. Du weisst das x und y bei dir theoretisch zwischen -2147483648 und 255 liegen koennen?Das war nur ein konstruiertes Beispiel. Daten die einer Klasse bei der Konstruktion uebergeben werden, sollten meiner Meinung nach in der Klasse selbst auf Gueltigkeit geprueft werden, da jede Klasse fuer ihre Daten selbst verantwortlich sein sollte.
@Jockelx: Ok, also wenn die check_range Methode wirklich nur diesen Parameter bearbeitet, macht es nix aus. Gut zu wissen.
P.S.: Es ist wirklich sehr anstrengend hier immer nur ohne Sonderzeichen posten zu koennen.
-
CppNeuland schrieb:
Das war nur ein konstruiertes Beispiel. Daten die einer Klasse bei der Konstruktion uebergeben werden, sollten meiner Meinung nach in der Klasse selbst auf Gueltigkeit geprueft werden, da jede Klasse fuer ihre Daten selbst verantwortlich sein sollte.
Edit:
Also ich persoenlich hatte den Fall den du beschreibst noch nicht.Ich persoenlich finde auch, dass der Owner der das Objekt erzeugt dafuer zustaendig ist fuer valide Werte zu sorgen.
Den Konstruktor auf "gut Glueck" aufzurufen und sich die Werte dann im Notfall hinzuschummeln ist vielleicht nicht so zielfuehrend.
Mich wuerde schon interessieren wie das andere machen
.
Ich persoenlich habe bis jetzt von einer Value "Ueberpruefung" im Konstruktor immer abgesehen denn:1. Musst du von irgendwoher erstmal einen nicht validen Wert erhalten.
2. Musst du auch erstmal in der Lage sein zu erkennen, dass es ein nicht valider Wert ist.
3. Biegst du den Wert auf etwas anderes um, was im Zweifel trotzdem nicht das ist, was der Nutzer eigentlich wollte bzw. mit dem dein Programm einfach weiterarbeiten kann ohne Folgefehler zu verursachen.P.S.:
Natuerlich ist jede Klasse fuer ihre Daten verantwortlich aber die x und y Werte in deinem Fall kommen nicht von deiner Klasse A.
An der Stelle wo du die x und y Werte berechnest oder bzw. wem die x und y Werte vorher gehoerten sollte die Validierung stattfinden. (nach meiner Meinung)
-
Unter anderem deswegen wurden doch auch Getter und Setter eingefuehrt, damit man eben nicht einfach den Werten von aussen vertraut wird, sondern damit sie vorher einen Check durchlaufen. Nur wuerde ich ungernen, nach dem erst die Instanz erzeugt wurde, durch Setter meiner Daten in die Members schreiben lassen, sondern dies gleich bei der Generierung machen lassen.
-
CppNeuland schrieb:
kurze_frage schrieb:
das "x > 255 ? 255 : x" koennte natuerlich auch eine Function sein.
A(int x, int y) : _x (rangeCheckd(x)) , _y (rangeCheckd(y)) { }dann schaut es eventuell freundlicher aus
Ist das nicht ein Problem? Also, Methoden aufzurufen, obwohl das Objekt in der Initialisierungsliste noch nicht vollstaendig konstruiert wurde? Gibt das nicht eventuell undefiniertes Verhalten?
Wie loest ihr das denn immer? extra-init Funktion? Ueber das neue C11 Feature?
das ist kein Problem, genau genommen ist rangeCheckd sogar eine const expression und sollte daher eigentlich ungefähr so
template <typename T > constexpr T rangeCheckd(T val, T max) { return val > max ? max : val ;} A(int x, int y) : _x (rangeCheckd(x,255)) , _y (rangeCheckd(y,255)) { }aussehen, dann is es auch wiederverwendbarer.
Syntax Korrektheit kann ich im Moment nicht garantieren da ich das jetzt nur so schnell hingeschrieben hab, aber ich denke das Konzept sollte klar sein.
edit:
eigentlich wollt ich rangeCheckd<255>(x) syntax, aber das sample war so falsch das ich es änderte
-
kurze_frage schrieb:
template <typename T , T max> constexpr T rangeCheckd(T val) { return val > max ? max : val ;} A(int x, int y) : _x (rangeCheckd<255>(x)) , _y (rangeCheckd<255>(y)) { }aussehen, dann is es auch wiederverwendbarer.
lol? Warum max als Template-Argument?
-
crunchexpr schrieb:
kurze_frage schrieb:
template <typename T , T max> constexpr T rangeCheckd(T val) { return val > max ? max : val ;} A(int x, int y) : _x (rangeCheckd<255>(x)) , _y (rangeCheckd<255>(y)) { }aussehen, dann is es auch wiederverwendbarer.
lol? Warum max als Template-Argument?
du warst zu schnell, siehe edit kommentar in meinem post
-
Ist denn das Range-Checken und das finale Verwenden in der Anwendungslogik in der selben Klasse?