klasse datum
-
hallo,
ich möchte eine klasse datum schreiben, bin mir aber bei meinem design nicht ganz sicher:class Datum { int tag, monat, jahr; long yeardays(int j); //wieviele Tage hat das angegebene Jahr long monthdays(int m, int j); // wieviele Tage hat der angegebene Monat 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 public: Datum() { Datum::tag=1;Datum::monat=1; Datum::jahr=1900; }; Datum(int tag, int monat, int jahr) { Datum::tag=tag; Datum::monat=monat; Datum::jahr=jahr; }; void setzeDatum(int tag, int monat, int jahr); // setzt Datum void naechstesDatum(const Datum &date, Datum &nextdate); // ermitteln des nächsten Datums void vorigesDatum(const Datum &date, Datum &beforedate); // ermitteln des vorigen Datums void operator+(int days); //zu Datum days Tage addieren void operator-(int days); //von Datum days Tage subtrahieren long diff(const Datum date1, const Datum date2); // Differenz in Tagen int comp(const Datum date1, const Datum date2); // date1 > date2 => -<Anz d. Tage> // date1 == date2 => 0 // date1 < date2 => +<Anz d. Tage> };
mir gehts vorallem um so fragen wie:
die difffunktion auf das interne datum bezogen oder 2 paramter (so wie jetzt)
die compfunktion auf das interne datum bezogen oder 2 paramter (so wie jetzt)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 ich jetzt endlich mal richtig C++ lerne, möchte ich mich von anfang an auf ein "schönes" klassendesign konzentrieren..
mfg
-
da ich jetzt endlich mal richtig C++ lerne, möchte ich mich von anfang an auf ein "schönes" klassendesign konzentrieren
ned nuer schoen sollt es sein, auch intelligent :p
Nur als anmerkung ... das Datum wird eigentlich von systemfunktionen abgedenkt, unter den meisten BS gibts nen Datentyp fuer.
Der Datentyp ist meisst nen integer, der die Tage als Diff zu einen "stichdatum" zaehlt (meist 01.01.1970) ...Die meisten systemfunktionen (WinAPI) wollen sowas als Datum haben. Um dir den ganzen umrechnungsschrott zu sparen, wuerd ich keinen neuen Datentyp erfinden wollen, sondern einfach nen Wrapper dafuer schreiben ....
(ne klasse die dir einen schon vorhandenen Datentyp kapselt, ihn so komfortabler macht, aber intern selber mit dem Datentyp arbeitet )Ciao ...
-
Ich bin (noch) kein Guru, aber vielleicht ein paar Anregungen, was mir aufgefallen ist.
- ich würd das Schlüsselwort private: nicht weglassen, selbst wenn man kann/darf.
- ein paar deiner Member liefern Anzahl Tage zurück, einmal als long und einmal als int. Das ist nicht durchdacht. (int comp, long monthdays)
- mach vor deine Member Variablen ein m_ hin. Damit vermeidest du solche verwirrten Sachen wie:
Datum(int tag, int monat, int jahr) { Datum::tag=tag; Datum::monat=monat; Datum::jahr=jahr; };
besser:
Datum(int tag, int monat, int jahr) { m_tag=tag; m_monat=monat; m_jahr=jahr; };
Der Scope-Operator innerhalb der eigenen Klasse ist sowieso verpöhnt.
Die beiden Methoden diff und comp sollten die Objekte wirklich als Referenz übergeben (wie bei vorigesDatum zB.)
Bei kleinen Objekten is es eigentlich egal, nur wenn die größer werden verbrät der Rechner Zeit mit kopieren(und wenns ne gute Note gibt, dann mach halt einfach immer Referenz)Gruß
electron
-
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.