klasse datum
-
class Datum
ok, guter name
{
int tag, monat, jahr;
schlecht!
in anbetracht der tatsache, daß die allermeinsten funktionsaufrufe
vergleiche und op+ und op- sind, man denke nur mal an die masse an
vergleichen, wenn mal ein datensatz in ner großen tabelle gesucht werden
soll, sollte die interne darstellung schon ein schlichter int sein.
in den fällen, wenn mal was auf den screen oder drucker ausgegeben werden
soll, stört die dazu jedesmal nötige umwandlung keinen, weil drucker und screen
unendlich lahm sind. da fällt das umwandeln gar nicht auf.
und sauviel code wird einfacher. vergleiche einfach mal
bool operator<(Datum const& a,Datum const& b)
{
if(a.year<b.year) return true;
if(a.year>b.year) return false;
if(a.month<b.month) return true;
if(a.month>b.month) return true;
return a.day<b.day;
}
gegen
bool operator<(Datum const& a,Datum const& b)
{
return a.days<b.days;
}long yeardays(int j); //wieviele Tage hat das angegebene Jahr long monthdays(int m, int j); // wieviele Tage hat der angegebene Monat
fast gut. funktionen sollten static sein, da sie kein datumsobjekt brauchen.
und notwendig sind sie ja zur eingabeüberprüfung.void date2days(const Datum &date, long &days); // Datum -> Tage seit 1.1.1900 void days2date(const long &days, Datum &date); // Tage seit 1.1.1900 -> Datum
unnotwendig, oder? sehe keine sinnvolle anwendung. also weg damit!
notfalligerweise kann der user auch mal sein datum von Datum(1,1,1900)
abziehen, um zu erfahren, wieviel zage dazwischen waren. nen feinen op-
machste ja.
und was soll der schwachsinnige referenzparameter?!
void date2days(const Datum &date, long &days); ist häßlich.
long date2days(const Datum &date); ist schön.das datum hab ich deswegen als referenz übergeben, weil unser professor gemeint hat er möchte nichts über einem integer per value übergeben werden sehen...
da hat er bestimmt was anderes gemeint.
public:
Datum()
ist ein defaultkonstruktor notwendig?
{ Datum::tag=1;Datum::monat=1; Datum::jahr=1900;
gewöhne die die verwendung der initialisiererliste an. und namensgleichheit ist in der
tat verwirrend. naja, hauptsache du machst nicht das doofe m_ davor.};
nutzloses semikolon, wie ich zu meiner schande gestehen muß.
Datum(int tag, int monat, int jahr) { Datum::tag=tag; Datum::monat=monat; Datum::jahr=jahr; };
hast nicht setzeDatum verwendet. wozu haste setzeDatum überhaupt? dafür ist der konstruktor
doch da!
hierdrinne mußte die tage seit 1.1.9000 oder 1.1.1970 in
deinem member abspeichern.void setzeDatum(int tag, int monat, int jahr); // setzt Datum
nutzlos und störend.
statt d.setzeDatum(1,2,2005); schreibt man d=Datum(1,2,2005);void naechstesDatum(const Datum &date, Datum &nextdate); // ermitteln des nächsten Datums
passend zur semantik deines op+ sollte hier statt
void naechstesDatum(const Datum &date, Datum &nextdate);
lieber stehen
Datum& operator++();void vorigesDatum(const Datum &date, Datum &beforedate); // ermitteln des vorigen Datums
entsprechend mit --
void operator+(int days); //zu Datum days Tage addieren void operator-(int days); //von Datum days Tage subtrahieren
rückgabe sollte das neue datum sein. mach'..s wie bei den ints.
hast du hier den operator+= und -= gemeint?long diff(const Datum date1, const Datum date2); // Differenz in Tagen
long operator-(Datum const& a,Datum const& b);
int comp(const Datum date1, const Datum date2); // date1 > date2 => -<Anz d. Tage> // date1 == date2 => 0 // date1 < date2 => +<Anz d. Tage>
ungewöhnlich. und operator< und operator== sind eh nützlicher, damit man die
Datums-Objekte in stl-container stecken kann.};
-
volkard schrieb:
wozu haste setzeDatum überhaupt? dafür ist der konstruktor
doch da!
...
nutzlos und störend.???
Das wundert mich jetzt, das von dir zu hören.
Bei Funktionen AendereDatumWonurZeigerVerfuegbar(Datum* d)
hilft der ctor nicht und bei so Sachen wieDatum d; for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); d.setzeDatum(tag, monat, jahr); // oder d = Datum(tag, monat, jahr); machWasMitDatum(d); }
ist der erste Fall doch auch performanter.
Begründe mal bitte deine Ablehnung der Setzefunktion.Jockel
-
Was genau ist da performanter?
Datum ist ein idealer Kandidat für ein immutable-Objekt.Zu dem m_ für Membervariablen: Halte ich für Schrott.
EDIT: Natürlich ist das erste performanter, weil du immer wieder ein Datum-Objekt im zweiten Beispiel kopierst. Aber wer Objekte kopiert, ist wirklich selber schuld.
EDIT2:
for (int i = i; i < 10000; ++i) { Datum d(tag, monat, jahr); machwas(d); }
-
Ähh? Das war doch gerade meine Kritik!
Wenn die einzige Möglichkeit Attribute zu ändern der Konstruktor ist, dann
muss man die Objekte wohl notgedrungen kopieren.Jockel
-
Dein Edit kam zu spät.
Okay, das mit dem kopieren war Mist. Trotzdem ist der Aufruf über den Konstruktor weiterhin langsamer, oder?Jockel
-
Nein, denke ich nicht. Der Konstruktor setzt ja im Grunde auch nur die Werte. Vielleicht wäre er langsamer, wenn es noch mehr Datenelemente gibt wie die 3, die wir ändern wollen.
Aber das ist IMO nichts gegen die Vorteile und Sicherheit, die unveränderliche Objekte bieten.
-
Jockelx schrieb:
volkard schrieb:
wozu haste setzeDatum überhaupt? dafür ist der konstruktor
doch da!???
Das wundert mich jetzt, das von dir zu hören.
...
Begründe mal bitte deine Ablehnung der Setzefunktion.oh, du hat mitgekriegt, daß ich speed-fanatiker bin. das ist fein.
triviale funktionen sind in der praxis doch inline. da hat der optimierer vollen zugriff, und es ist einfach gleich, ob ich schreibe
Datum d; for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); d.setzeDatum(tag, monat, jahr); machWasMitDatum(d); }
oder
Datum d; for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); d = Datum(tag, monat, jahr); machWasMitDatum(d); }
oder
int d[]={1,1,1900}; for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); a[0]=tag;d[1]=monat;d[2]=jahr; machWasMitDatum(d); }
es wird exaktamente der gleiche assembler-code erzeugt.
solange es bei null-kosten bleibt, feines c++ zu nehmen, soll man das auch tun, finde ich.und nu zu besserem beispiel.
das hier darf keiner schreiben:Datum d; for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); d = Datum(tag, monat, jahr); machWasMitDatum(d); }
d ist viel, viel zu unlokal.
mußt schon schreiben:for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); Datum d = Datum(tag, monat, jahr); machWasMitDatum(d); }
aha, und jetzt apiert sogar der zweitdümmste compiler, daß hier ne returnvalue-optimierung zu greifen hat. es wird nicht ein datumsobjekt erzeugt, davon ne kopie gemacht und das original gelöscht. nee, es wird direkt in den speicher vom lokalen objet d reinkonstruiert.
schon wieder nullkosten für abstraktion. und der trick geht auch mit größeren klassen.
-
Mal ne kurze Design-Frage, wo wir schon dabei sind:
Sollte man bei einem Immutable-Objekt überhaupt die Operatoren, die this verändern (+=, *= etc) implementieren?
-
interpreter schrieb:
Mal ne kurze Design-Frage, wo wir schon dabei sind:
Sollte man bei einem Immutable-Objekt überhaupt die Operatoren, die this verändern (+=, *= etc) implementieren?
war mit immutable gemeint, daß man schreiben sollte:
for(i=0; i<1000000; ++i) { leseDatum(tag, monat, jahr); Datum const d = Datum(tag, monat, jahr); machWasMitDatum(d); }
falls immutable das ist, dann klar, hab das const da vergessen. mit const ist ja alles noch viel sicherer.
und += und *= sind ja non-const members und würden auf dem const d gar nicht laufen, also kann man sie ruhig implementieren.
-
Wenn man ein Objekt als immutable designt, dann darf man es nach dessen Erzeugung nicht mehr verändern können. Sowas wie X::setY(int y) darf es also nicht geben. Folglich darf ich doch auch operator*= usw nicht implementieren, weil ich sonst das Objekt selbst verändern kann. Ob das Objekt nun const oder nicht is, is doch egal - auch "nicht const"-Objekte dürfen nicht mehr verändert werden.
BTW:
Sowas sehe ich zum 1. Mal:Datum const d = Datum(tag, monat, jahr);
Wieso schreibst du das const nach dem Datentyp? Ist das selbe, wie const Datum d? Kann es sein, dass in der Zeile zuerst ein Temporäres Objekt erzeugt wird, das dann per CopyConstructor in d kopiert wird?
-
interpreter schrieb:
Wieso schreibst du das const nach dem Datentyp? Ist das selbe, wie const Datum d?
Ja.
http://tutorial.schornboeck.net/konstanten.htm schrieb:
Statt int const kann man auch const int schreiben, beides bedeutet das selbe. Allerdings sollte man bei der Variablendeklaration immer von Rechts nach Links lesen, so dass man float const PI so liest: (Die Variable) PI ist vom Typ const float.
interpreter schrieb:
Kann es sein, dass in der Zeile zuerst ein Temporäres Objekt erzeugt wird, das dann per CopyConstructor in d kopiert wird?
siehe Humes FAQ
-
Shade Of Mine schrieb:
interpreter schrieb:
Wieso schreibst du das const nach dem Datentyp? Ist das selbe, wie const Datum d?
Ja.
http://tutorial.schornboeck.net/konstanten.htm schrieb:
Statt int const kann man auch const int schreiben, beides bedeutet das selbe. Allerdings sollte man bei der Variablendeklaration immer von Rechts nach Links lesen, so dass man float const PI so liest: (Die Variable) PI ist vom Typ const float.
interpreter schrieb:
Kann es sein, dass in der Zeile zuerst ein Temporäres Objekt erzeugt wird, das dann per CopyConstructor in d kopiert wird?
siehe Humes FAQ
Danke.
Da finde ich aberconst Datum d(tag, monat, jahr);
wesentlich intuitiver
-
interpreter schrieb:
Wenn man ein Objekt als immutable designt, dann darf man es nach dessen Erzeugung nicht mehr verändern können. Sowas wie X::setY(int y) darf es also nicht geben. Folglich darf ich doch auch operator*= usw nicht implementieren, weil ich sonst das Objekt selbst verändern kann. Ob das Objekt nun const oder nicht is, is doch egal - auch "nicht const"-Objekte dürfen nicht mehr verändert werden.
aha. und wozu macht man sowas?
auf jeden fall ist immutable für Datum-objekte unfug. Datum muß genauso praktisch wie time_t sein. Ware einfach störend, statt d=d+14 nicht mehr d+=14 schreiben zu dürfen.