Operatoren für eigene Double Klasse
-
Dann sag das mal Microsoft ;).
Das Problem ist wenn ich z.b. einen Style aus einer XAML Datei auswerte dann gibt es dort z.B. setter mit denen man Vaiablen setzen kann. Das kann die Width sein oder aber auch das ControlTemplate. Ich finde diese Lösung eigentlich ganz schön. Wenn ich es anders lösen muss wird es erst recht gruselig, mal davon abgesehen dass ich gar nicht weis wie ich es anders machen könnte.
-
Student83 schrieb:
Dann sag das mal Microsoft ;).
Nein, das sag ich dir und deine Problembeschreibung bestätigt dies.
mal davon abgesehen dass ich gar nicht weis wie ich es anders machen könnte.
Dies wird's wohl sein. Virtuelle Funktionen wurden genannt, etwas anderes ist Überladung. Um zu entscheiden was geeignet ist, fehlt der Kontext. Aber Typprüfung zur Laufzeit, noch dazu ohne Polymorphie, ist ein ganz deutliches Alarmsignal dass man etwas falsch* macht.
*: Falsch im Sinne von schlecht. Dein Programm mag durchaus so funktionieren, aber es ist nicht gut in fast jeder Hinsicht.
-
Das mit den virtuellen Funktionen ist ein gutes Stichwort, allerdings muss ich den Inhalt des
<Setter Property="Irgendwas"><Setter.Value>...Inhalt...</Setter.Value></Setter>ja irgendwo im Speicher ablegen. Das ist die Variable
Object value;in der Setter-Klasse.
-
Vor einiger Zeit habe ich ein paar Punkte genannt, warum explizite Typunterscheidungen schlecht sind:
Nexus schrieb:
Einerseits sind Typunterscheidungen sehr fehleranfällig und müssen ständig mit der Klassenhierarchie konsistent bleiben. Wenn du Klassen berarbeitest, brauchst du an einem anderen Ort nochmals Code zu ändern. Bei dynamic_cast hast du zudem das Problem, dass die Reihenfolge der if-else-Statements richtig sein muss (abgeleitete müssen vor Basisklassen stehen). Bei typeid tritt das zwar nicht auf, dafür muss der Typ exakt stimmen, du kannst also mit Basisklassenabfragen keine abgeleiteten Klassen einschliessen. Ein weiteres Problem, das die Zentralisierung des Verhaltens mit sich bringt, ist die starke Abhängigkeit von Code. Bei der Typabfrage müssen nämlich alle Klassen vollständig bekannt sein. Besonders bei grösseren Projekten kann das die Kompilierzeit massiv beeinträchtigen, im Falle mehrerer Typabfragen in unterschiedlichen Modulen umso mehr.
Das Wichtigste ist aber wohl, dass explizite Typunterscheidungen den Grundsatz der Polymorphie verletzen. Polymorphie (nicht nur dynamische) erlaubt es einem, Objekte mit unterschiedlichem Verhalten einheitlich anzusprechen, was eine starke Abstraktion ermöglicht. Mit manuellen Fallunterscheidungen verwirft man dieses Konzept und geht einen Schritt zurück – weg von objektorientierter Programmierung, in der ein Objekt selbst für sich schaut.
-
Gut, aber was wäre denn dann die Alternative? Ich müsste für jede Variable die ein Setter setzen kann eine eigene Setter Klasse schreiben die dann den den entsprechenden Setter.value speichert. Also z.B.
class IntSetter { int value; } class DoubleSetter { double value; } class ControlSetter { Control value; } //50 Steuerelemente später... class TabItemSetter { TabItem value; }Und dann stehe ich wieder vor dem selben Problem. Ich muss nämlich (explizit) feststellen von welchem Typ der Setter ist wenn ich ihn anwenden will also den Setter.value abrufen möchte.
Was mir noch einfällt wäre ich gebe der Setter Klasse für jeden Datentyp einen Zeiger:
class Setter { int *value; double *value; Control *value; //... TabItem *value; }Allerdings muss ich ja dann auch wieder eine Rückgabefunktion haben für jeden Zeiger. Den zurückgegebenen Zeiger prüfe ich dann, ob er ungleich null ist. Das ist einfach Wahnsinn. Das würde den Code enorm aufblähen.
-
struct Foo { int a; double b; void set(double bb){b=bb;} void set(int aa){a=aa;} }; int main() { Foo bar; bar.set(5); bar.set(3.124); cout<<bar.a<<' '<<bar.b<<'\n'; }
-
Kann es sein dass wir gerade aneinander vorbeigeredet haben. Also bei mir ist jede Klasse im Projekt von der Klasse "Object" abgeleitet. Ist
(Double)objdann überhaupt eine explizite Typumwandlung da ja Double von Object abgeleitet ist?
Ist auch eigentlich egal. Eignetlich wollte ich ja wissen welche Operatoren ich der Klasse im ersten Post noch hinzufügen/entfernen muss damit die Klasse mit dem Datentyp "double" zusammenspielt.
-
So ich habs. Ich musste einige Operatoren rausschmeißen und den Operator
operator double() const;hinzufügen. Jetzt funktioniert es so wie ich es mir vorgestellt habe. Ob die Rechenoperationen auch richtig sind habe ich allerdings noch nicht getestet ;).
class Double : public Object { public: Double(); Double(double value); //Double(float value); //Double(int value); private: double value; public: //friend bool operator==(Double const& lhs, Double const& rhs); //friend bool operator<(Double const& lhs, Double const& rhs); Double& operator=(const Double& rhs); Double& operator++(); const Double operator++(int); Double& operator--(); const Double operator--(int); Double& operator+=(Double const& rhs); Double& operator-=(Double const& rhs); Double& operator*=(Double const& rhs); Double& operator/=(Double const& rhs); operator double() const; /*operator float() const; operator int() const;*/ };Erstaunlich das die ganzen Operatoren nicht notwendig waren:
/*const Double operator+(Double const& lhs, Double const& rhs); const Double operator-(Double const& lhs, Double const& rhs); const Double operator*(Double const& lhs, Double const& rhs); const Double operator/(Double const& lhs, Double const& rhs); bool operator!=(Double const& lhs, Double const& rhs); bool operator<(Double const& lhs, Double const& rhs); bool operator>(Double const& lhs, Double const& rhs); bool operator<=(Double const& lhs, Double const& rhs); bool operator>=(Double const& lhs, Double const& rhs);*/Was ich jetzt aber noch gerne wüsste ist, ob diese Klasse eurer Meinung nach noch verbesserungswürdig ist und ob die folgende Umwandlung nun implizit oder explizit ist?
class Double : public Object {...} Double *d = new Double(); Object *obj = d; //Implizit oder explizit?
-
Kann evtl. ein Moderator die hier entstandene Diskussion abtrennen. Meine Fragen gehen ganz unter ;).
-
Zu dir, Student83:
const Doublewürde ich nicht als Rückgabetyp verwenden.Doublereicht. Denn(a + b) = c;ist kein Fehler, der immer wieder passiert und verboten gehört. Auch wenn es immer noch Leute gibt, die so argumentieren.- Wenn deine Klasse nur einen einzigen
doubleenthält, würde ich sie jeweils als Kopie und nicht als Referenz aufconstübergeben.
Und warum sind die globalen Operatoren nicht nötig?
Bei diesem Beispiel
Double *d = new Double(); Object *obj = d;führst du mit der zweiten Zeile einen impliziten Upcast von
Doublezur BasisklasseObjectdurch. Upcasts (in Richtung Basisklasse) sind immer implizit möglich (ausser bei Mehrdeutigkeiten durch Diamonds of Death, aber das ist hier unwichtig).
-
Nexus schrieb:
Zu dir, Student83:
[list][*]const Doublewürde ich nicht als Rückgabetyp verwenden.Doublereicht. Denn(a + b) = c;ist kein Fehler, der immer wieder passiert und verboten gehört. Auch wenn es immer noch Leute gibt, die so argumentieren.Die Operatoren hatte ich so mehr oder weniger alle von hier genommen:
www.c-plusplus.net/forum/232010
und das const einfach übernommen. Das mit dem const ist mir sowieso noch ein Rätsel. Habe da schon zig unterschiedliche Versionen gesehen:const Double operator/(Double const& lhs, Double const& rhs); Double operator/(Double const& lhs, Double const& rhs) const; const Double operator/(const Double& lhs, const Double& rhs);Gibt es da eine Regel wo das const denn nun hingeschrieben wird?
Nexus schrieb:
Und warum sind die globalen Operatoren nicht nötig?
Das wüsste ich auch gern. Zumindest funktioniert es jetzt tadellos. Klammere ich die Operatoren wieder aus bekomme ich Fehler wie:
"error C2666: '::operator -' : 2 overloads have similar conversions"Noch eine weitere Frage. Und zwar habe ich den "="-Operator folgendermaßen implementiert:
void swap(Double& a, Double& b) { Double c(a); a=b; b=c; } Double& Double::operator=(const Double& rhs) { if (this != &rhs) { Double tmp(rhs); swap(*this, tmp); } return *this; }Was dazu führt dass sobald ich einem Double einen Wert zuweise (z.B. Double d = 42) sich das Programm ohne Fehlermeldung einfach beendet. Der Fehler liegt wohl an der swap-Funktion. Klammere ich diese aus geht es. Auch mit std::swap(*this, tmp); beendet sich das Programm automatisch.
Und warum schreibt man eigentlich nicht stattdessen nur:
Double& Double::operator=(const Double& rhs) { value = rhs.value; return *this; }So brauche ich keine temporäre Variable der ich noch den Wert von rhs zuweisen muss. Und Swappen brauche ich auch nicht?
Und noch ein Hinweis an die Moderatoren. Bitte verlängert doch mal die Zeit nach der man hier automatisch abgemeldet wird. Ich habe es noch nicht geschafft einen Beitrag zu schreiben ohne dass ich mich neu einloggen musste ;).