Eine kleine Umfrage: C/C++ the OOP way?
-
//mit default-ctor, der objekt löschbar macht Bitmap b;//zeitverschwendung loadFromBmp(&b,"test.bmp");//ok
die zeitverschwendung, b erst löschbar zu machen und dann doch was anderes reinzumachen, ist doch doof. sagen wir mal, die verbietet sich, weil wir auch sehr schnellen code haben wollen (deshalb verbieten sich für mich ja auch pimpl und rimpl).
Jup, das sieht meinem Code am ähnlichsten.
Das Löschbarmachen ist in diesem Fall der Defaultkonstruktor eines Vektors - klar ist das unnötig, aber im Vergleich zum Öffnen und Parsen einer kompletten Datei doch absolut vernachlässigbar... Da geht bei mir das Design vor und für jeden Dateitypen einen Konstruktor hinzuzufügen finde ich nicht sehr elegant.die stl-algos sind es weniger, aber algos rufen auch nicht immer danach, oo zu sein.
Die Container sind IMHO gute OO, eben weil die Algos ausgelagert sind. In anderen Sprachen haben Containerklassen einen Haufen Methoden (die alle Zugriff auf die Interna haben) und dadurch auch einen viel zu großen Aufgabenbereich.
-
volkard schrieb:
Alles weitere (drawText, loadFromBMP, saveToPNG usw. etc.) sind freie Funktionen.
überhaupt gar kein ack. loadFromBMP klingt für mich stark danach, als sollte das ein ctor sein.
ich dachte da eher mehr an
Bitmap b(loadFromBMP("nix.bmp"));
da könnte Bitmap nen Ctor für rohe daten haben - loadFrom* ladet die Datei und liefert die Rohen daten zurück.
Man hätte somit eine Art BitmapHandle - das in loadFrom* erstellt wird und in Bitmap einfach übernommen wird.
Wäre soetwas OK?
-
Shade Of Mine schrieb:
ich dachte da eher mehr an
Bitmap b(loadFromBMP("nix.bmp"));
da könnte Bitmap nen Ctor für rohe daten haben - loadFrom* ladet die Datei und liefert die Rohen daten zurück.
Man hätte somit eine Art BitmapHandle - das in loadFrom* erstellt wird und in Bitmap einfach übernommen wird.
Wäre soetwas OK?
Das was du jetzt Bitmap-Handle nennst, wäre dann ja quasi das eigentliche Bitmap-Objekt.
Da finde ich Volkards Ansatz von Bitmap und FileBitmap besser.
-
DrGreenthumb schrieb:
Da finde ich Volkards Ansatz von Bitmap und FileBitmap besser.
Nur hat man da das Problem, dass man Polymorphie braucht um beliebige Dateien (Datei Typen) zu laden.
-
typedef unsigned char byte_t; class bitmap { byte_t *data_m; public: template<class Loader> bitmap(const char *str) : data_m(Loader(str)) { } ~bitmap() { delete []data_m; } }; byte_t *bmp_loader(const char *str) { /*...*/ return 0x0; } int main() { bitmap bit<bmp_loader>("demo.bmp"); }
?
-
Konstruktor mit Template-Argument geht leider nicht. Stolper ich auch immer wieder drüber
-
ach verdammt (btw. das wär was für langX ;))
-
wär zwar nichts für container, aber mithilfe eines 2. parameters sollte es doch gehen oder?
oder ner static methode, welche es erlaubt, den loader im voraus festzulegen, aber das wär wiederum nichts, wenns ums schnelle einfügen geht...
3. möglichkeit wär natürlich, dass der teil, welcher die datei ausliest, je nachdem welcher typ das ist, einen weiteren char vorpackt, und man dann intern einfach nen switch hat, aber das wär ja fast dasselbe wie die erste möglichkeit. Ich glauba n dieser stelle kommt man wohl nicht an der polymorphie vorbei, wenn mans "schön" machen will
-
operator void schrieb:
klar ist das unnötig, aber im Vergleich zum Öffnen und Parsen einer kompletten Datei doch absolut vernachlässigbar...
ja, bei den bitmaps kannste in sachen performance hudeln wie du magst. einmal detei-öffnen und du hast eh schon 100000000 takte weg. wozu hier und da 100 sparen? ist hier völlig unnötig.
dann bleibt bei den bitmaps nur noch der grund, daß man default-konstruktoren überhaupt meiden sollte.
bei anderen klassen, die vielleicht nicht von nem filename, sondern von einem bereits offenen istream& erzeugt/geladen werden, kann das mit der performance schon ganz anders aussehen.[quote] Da geht bei mir das Design vor und für jeden Dateitypen einen Konstruktor hinzuzufügen finde ich nicht sehr elegant.[quote]
ob du nen konstruktor oder ne ladefunktion baust, ist doch vom aufwand kein unterschied.Die Container sind IMHO gute OO, eben weil die Algos ausgelagert sind. In anderen Sprachen haben Containerklassen einen Haufen Methoden (die alle Zugriff auf die Interna haben) und dadurch auch einen viel zu großen Aufgabenbereich.
naja, aber ich *denke* sachen wie "und dann sortiere ich die liste" aber ich *schreibe* NICHT "list.sort()" oder das gleichwertige "sort(&list)", sondern einen unfug wie "sort(list.begin(),list.end())". sollte ich wirklich *gedacht* haben "und dann sortiere ich mal den bereich vom anfang der liste bis zum ende der liste". nee, dazu sind meine drogen gar nicht gut genug.
-
kingruedi schrieb:
bmp_loader...
eigentlich nicht.
aber ist auch unerheblich. hätte nicht gedacht, daß ihr so konkrete (und gute) vorschläge macht, wie es weitergehen würde.
vermutlich isses wichtig, sich erstmal klar zu werden, was ne Bitmap, die jeder anfassen kann, sein soll. vermutlich doch die klasse, die auch sich laden kann. dann müßte man halt FileBitmap zu Bitmap umbenennen, nachdem man Bitmap zu BitmapData /Canvas/Plane/Pixelbuffer... umgenannt hat.
-
volkard schrieb:
dann bleibt bei den bitmaps nur noch der grund, daß man default-konstruktoren überhaupt meiden sollte.
Und woher kommt der Grund? Das klingt wie eine allgemeine Faustregel.
bei anderen klassen, die vielleicht nicht von nem filename, sondern von einem bereits offenen istream& erzeugt/geladen werden, kann das mit der performance schon ganz anders aussehen.
Bitmaps kann man auch von Streams laden, den Dateinamen hast du zuerst ins Spiel gebracht :p
Wie das allgemeine Schema von Bitmap in irgendeinem Fall ein größeres Performanceproblem darstellen kann, sehe ich aber gerade nicht. Allgemein haben dynamische Resourcen intern doch einfach einen Zeiger (in diesem Fall in einen std::vektor verpackt), der im Defaultkonstruktor unnötig genullt wird - damit das stört, muss man schon in einen _sehr_ kleinen Bereich gehen.ob du nen konstruktor oder ne ladefunktion baust, ist doch vom aufwand kein unterschied.
Nein, aber vom Designstandpunkt aus IMHO schon. Dann muss man sich wohl überlegen, ob einem eine klare Aufgabentrennung ein paar Nullungen wert ist.
naja, aber ich *denke* sachen wie "und dann sortiere ich die liste" aber ich *schreibe* NICHT "list.sort()" oder das gleichwertige "sort(&list)", sondern einen unfug wie "sort(list.begin(),list.end())". sollte ich wirklich *gedacht* haben "und dann sortiere ich mal den bereich vom anfang der liste bis zum ende der liste". nee, dazu sind meine drogen gar nicht gut genug.
Ich sage ja auch nicht, dass die STL perfekt ist - das Sequenzkonzept ist sicher in vielen Fällen nervig. Aber als Beispiel für klare Aufgabenverteilung und Datenkapselung ist sie nicht schlecht.
-
volkard schrieb:
naja, aber ich *denke* sachen wie "und dann sortiere ich die liste" aber ich *schreibe* NICHT "list.sort()" oder das gleichwertige "sort(&list)", sondern einen unfug wie "sort(list.begin(),list.end())". sollte ich wirklich *gedacht* haben "und dann sortiere ich mal den bereich vom anfang der liste bis zum ende der liste". nee, dazu sind meine drogen gar nicht gut genug.
Nun, bisweilen kann man sich ja damit behelfen (je nach Können des Compilers):
#include <algorithm> #include <vector> #include <list> template<class T, class A, template<class,class> class C> void sort(C<T,A>& c) { std::sort(c.begin(), c.end()); } template<class T, class A> void sort(std::list<T,A>& c) { c.sort(); } void foo() { std::vector<int> v; sort(v); std::list<int> l; sort(l); }
-
operator void schrieb:
volkard schrieb:
dann bleibt bei den bitmaps nur noch der grund, daß man default-konstruktoren überhaupt meiden sollte.
Und woher kommt der Grund? Das klingt wie eine allgemeine Faustregel.
Weil doch auch ein leeres Bitmap recht zeitintensiv sein kann.
Es sei denn du erstellst im Default Ctor gar kein Bitmap sondern lässt das Objekt in einem 'nicht vorhanden'-status. Das wäre natürlich auch sehr schlecht.
-
DrGreenthumb schrieb:
Konstruktor mit Template-Argument geht leider nicht. Stolper ich auch immer wieder drüber
Geht natürlich.
-
?!?!? schrieb:
DrGreenthumb schrieb:
Konstruktor mit Template-Argument geht leider nicht. Stolper ich auch immer wieder drüber
Geht natürlich.
Doch nicht. lol
-
Shade Of Mine schrieb:
Weil doch auch ein leeres Bitmap recht zeitintensiv sein kann.
Es sei denn du erstellst im Default Ctor gar kein Bitmap sondern lässt das Objekt in einem 'nicht vorhanden'-status. Das wäre natürlich auch sehr schlecht.
Dass es so sein _kann_ ist aber keine Begründung dafür, Default-Konstruktoren allgemein zu meiden - darauf zielte meine Frage ja ab. (Siehe volkards Originalposting.)
Um meine Bitmap als Beispiel totzutreten, der Default-Konstruktor setzt width und height (unsigneds) auf 0 und der pixels-Vektor wird default-konstruiert. (Bei Bedarf könnte man den Vektor sogar noch durch was Sparsameres ersetzen.)
-> Alle Klasseninvarianten erfüllt (ohne magische Flags), kein Overhead der im normalen Umfeld einer Bitmap relevant ist. Ich sehe nach wie vor kein Problem
-
Um überhaupt noch beim Fachsimpeln ein WENIG mitreden zu können. In Java benutze ich fast nie def ctors. Liegt aber einfach daran, daß ich meine Klassen in der Regel fast immer Daten von außen von Nöten sind. Hat jetzt aber echt nichts mit Performance zutun.
-
Shade Of Mine schrieb:
Blue-Tiger schrieb:
im Allgemeinen steckt bei mir (fast) alles in Klassen. Oft passierts mir z. B., dass 3 oder 4 Funktionen auf die gleichen Variablen zugreifen muessen, das endet dann meist in einer Klasse mit den Variablen als Membern und den Funktionen als Methoden
halte ich für fragwürdig.
denn ergibt das dann auch ein Objekt?
oder ist es dann sowas:Hash h; h.do_hash("abc"); return h.value();
Ich hab nie selbst einen Hash implementiert (auch nie wirklich benutzt; sprich ich hab kaum Ahnung davon, was ein Hash ist), aber dast klingt do so, aus bräuchte man nur eine einzige Funktion do_hash(), die genau das zurueckliefert, was h.value() zurueckliefern wuerde.
Ich versteh jetzt also nicht, was an meiner Denkweise falsch ist (vielleicht kannst du's ja an einem anderen Beispiel erklaeren):
Ideel: wenn Funktionen gemeinsame Daten bearbeiten, scheinen sie zusammenzugehoeren. Ob jetzt nur die gemeinsamen Daten oder Daten & Funktionen zusammen eine Einheit (Klasse) bilden, darueber koennte man dann streiten
Praktisch: lieber als x Funktionen die selben 4 Argumente mitzugeben, pack ich alles zusammen in eine Klasse, die jene 4 Werte als Ctor-Argumente erhaelt, das ist uebersichtlicher und IMO eine bessere Loesung als z. B. globale Varialben
Korrigiert & kritisiert mich, wenn ich mit dieser Meinung total daneben liege
@Wan-Hi: wenn du keine Default-Konstruktoren hast, wie legst du dann Arrays von Objekten an?
-
Blue-Tiger schrieb:
Ich hab nie selbst einen Hash implementiert (auch nie wirklich benutzt; sprich ich hab kaum Ahnung davon, was ein Hash ist), aber dast klingt do so, aus bräuchte man nur eine einzige Funktion do_hash(), die genau das zurueckliefert, was h.value() zurueckliefern wuerde.
OK, das Beispiel war nicht super - aber so eine Hash (oder war es MD5?) Klasse gibt es wirklich. Sie wird sogar verwendet...
Natürlich reicht hier eine Funktion. Aber was würdest du machen wenn es 3 Funktionen gäbe?
do_hash
un_hash (OK, gibts nicht, ist aber nur n Beispiel)
invert (Wozu auch immer ;))OK, mir fällt nichts besseres ein.
Im Prinzip meine ich damit: nur weil etwas zusammengehört, gehört es noch lange nicht in eine Klasse.
Ich versteh jetzt also nicht, was an meiner Denkweise falsch ist (vielleicht kannst du's ja an einem anderen Beispiel erklaeren):
Ich weiss nicht ob deine Denkweise falsch ist - ich meine nur, dass es kritisch sein kann, wenn du deine Aussage zu ernst nimmst.
Ideel: wenn Funktionen gemeinsame Daten bearbeiten, scheinen sie zusammenzugehoeren. Ob jetzt nur die gemeinsamen Daten oder Daten & Funktionen zusammen eine Einheit (Klasse) bilden, darueber koennte man dann streiten
Manchmal - aber oft macht eine Klasse keinen sinn, wie bei meinem verunglückten Hash Beispiel.
Praktisch: lieber als x Funktionen die selben 4 Argumente mitzugeben, pack ich alles zusammen in eine Klasse, die jene 4 Werte als Ctor-Argumente erhaelt, das ist uebersichtlicher und IMO eine bessere Loesung als z. B. globale Varialben
Aber dadurch musst du kein Objekt haben.
zB
get_mwst()
get_kest()
get_wasweissich()sind auch sehr ähnlich. Sie geben alle einen Steuersatz basierend auf ähnlichen Daten aus. Dennoch passen sie nicht in eine Klasse. Weil sie zwar zusammengehören, aber keinen 'state' haben. Und 'stateless' Objekte machen idR keinen Sinn.
@operator void:
du hast mindestens die Kosten einer sinnlosen allokierung.
Klar, so viel ist es nicht - aber es sind dennoch sinnlose kosten. Weil du von vornherein weisst, dass du die Defaultwerte sowieso wegschmeisst.Du gibst einen Text ja auch nicht zum probelesen weg, wenn du weisst, dass du ihn sowieso neu schreibst, oder ?
-
Shade Of Mine schrieb:
Ideel: wenn Funktionen gemeinsame Daten bearbeiten, scheinen sie zusammenzugehoeren. Ob jetzt nur die gemeinsamen Daten oder Daten & Funktionen zusammen eine Einheit (Klasse) bilden, darueber koennte man dann streiten
Manchmal - aber oft macht eine Klasse keinen sinn, wie bei meinem verunglückten Hash Beispiel.
Praktisch: lieber als x Funktionen die selben 4 Argumente mitzugeben, pack ich alles zusammen in eine Klasse, die jene 4 Werte als Ctor-Argumente erhaelt, das ist uebersichtlicher und IMO eine bessere Loesung als z. B. globale Varialben
Aber dadurch musst du kein Objekt haben.
zB
get_mwst()
get_kest()
get_wasweissich()sind auch sehr ähnlich. Sie geben alle einen Steuersatz basierend auf ähnlichen Daten aus. Dennoch passen sie nicht in eine Klasse. Weil sie zwar zusammengehören, aber keinen 'state' haben. Und 'stateless' Objekte machen idR keinen Sinn.
Ich glaube, damit waren eher set-Funktionen gemeint, um die Invarianten zu bewaren, oder habe ich ihn da falsch verstanden?