STL container und abstrakte Datentypen - praktische tips
-
hoijui schrieb:
...
Ihr unterschaetzt meine Macht, junger Sternen-Geher! ...Mitnichten, junger C++-Padawan.

hoijui schrieb:
...ich kann sowohl den rueckgabewert der funktion sowie den type all meiner membervariablen nach belieben waehlen! ich werde sie gleich waehlen....
Ja schon, aber bei einer "map<const IA*, const IB*>" als Member kannst Du ja auch innerhalb der Klasse nicht schreibend auf die Elemente zugreifen ... vielleicht willst Du das ja auch nicht, aber darauf wollte ich nur hinweisen.
Gruß,
Simon2.
-
Simon2 schrieb:
Du machst Dir vermutlich gar nicht bewusst, wie oft Copy-Konstruktoren verwendet werden ... und in wie vielen Fällen Dein "Sonderverhalten" in die Hose gehen kann, gerade weil es total dem "Usus" widerspricht. in Java würdest Du doch vermutlich auch nicht
clone() { Festplatte_formatieren(); }implementieren...
hmm.. ich hab keine ahnung was du damit meinst

ich hab aber schon eine ahnung wie oft copy konstruktoren verwendet werden. Weill miene IA klasse naehmlich keinen hat, kriege ich eine compiler fehlermeldung an jedem ort wo dieser den copy-ctor aufrufen will

ist die idee eigentlich, dass der IA copy-ctor gar nie benutzt werden muss? soll ich desshalb alles mit pointern machen?
-
hehe simon!

ich bin der uebermuetige, ueberhebliche lehrling.. jaja.. hast ja recht ;-DWas ich aus der diskussion um map gelernt habe, ist, dass map<const IA*, const IB*> eigentlich keinen sinn gibt. weill man den gleichen effekt mit const map<IA*, IB*> bekommt, und da braucht man dann eben nicht umzukopieren. ich kann mich auch nicht erinnern, schonmal eine map mit const key oder value gesehen zu haben (aussert bei const char*). es waehre eigentlich noch sinnwoll, wenn das in tutorials fuer C++ STL containers erwaehnt wuerde, hab ich aber bis jetzt noch nie gelesen. klar, man kann es aus dem code ableiten... aber da muss man zuerst mal auf die idee kommen das es so sein koennte, und dann alle methoden durch gehen, um zu schauen ob die bei const auch wirklich keine moeglichkeit zum veraendern der keys und values erlauben.
-
hoijui schrieb:
Was ich aus der diskussion um map gelernt habe, ist, dass map<const IA*, const IB*> eigentlich keinen sinn gibt. weill man den gleichen effekt mit const map<IA*, IB*> bekommt
ähm nein.
- bei einer map<const IA*, const IB*> hast du eine Map mit zeigern auf konstante Objekte. Du kannst der map sowoghl zeiger auf weitere Konstante Objkete hinzufügen und entfernen, als auch die Zeiger, die schon in der map sind, umbiegen auf andere konstante Objkete
- const map<IA*, IB*> heißt, dass du weder neue Zeiger hinzufügen kannst noch die Zeiger umbiegen kannst (weil ein Zugriff auf eine const map nur const values, also konstante Zeiger liefert) was du sehr wohl kannst ist die Objekte zu verändern, auf die die Zeiger verweisen, denn die Objkete selbst sind nicht const. Somit sind die bieden das genaue gegenteil, const-bezüglich
- der vollständigkeit halber: map<IA * const, IB * const> wäre eine weitere Alternative. Hier kannst du die Zeiger nicht umbiegen (sind ja const), kannst aber sehr wohl weitere konstante zeiger einfügen und entnehmen (die map selbst ist nicht const), und auch die Objkete sind veränderbar.
-
pumuckl schrieb:
...
- const map<IA*, IB*> ... was du sehr wohl kannst ist die Objekte zu verändern, auf die die Zeiger verweisen, denn die Objkete selbst sind nicht const. Somit sind die bieden das genaue gegenteil, const-bezüglich
...Hmmm also ich glaube nicht.
In der "Container-Theorie" schon (bei einem Array geht das vermutlich) ... aber Du bekommst bei einer const-map keine veränderbaren Referenzen auf die Elemente.
Oder habe ich da was übersehen ?Gruß,
Simon2.
-
ja, hab das eben auch so verstanden wie simon. von einer const map bekommt man nur const_iterators, keine iterators, und auch operator[] kann man nicht verwenden, weill der nicht const ist (hat simon schon erklaehrt). wie soll man also die elemente veraendern koennen?
die einzige weise die ich sehe ist, wenn man pointer drinn hat, dass dann nur die pointer konstant sind, nicht aber die daten selber, auf die sie zeigen, wenn man sie ueber den const_iterator aus der map holt. ist das so?
-
Simon2 schrieb:
In der "Container-Theorie" schon (bei einem Array geht das vermutlich) ... aber Du bekommst bei einer const-map keine veränderbaren Referenzen auf die Elemente.
Oder habe ich da was übersehenJa, hast du. Die Elemente der Map sind Pointer, nicht die Daten selbst. Die Pointer sind wie geschrieben nicht veränderbar, aber dereferenzierung ändert den Pointer ja auch nicht. Worauf diese Pointer zeigen ist aber durchaus veränderbar. Das ist der unterschied zwischen
const T*; //pointer auf ein const objekt T* const; //const pointer auf ein durchaus veraenderbares Objektnehmen wir einen konstanten Container<T> wie im Beispiel, dann ist T in dem Fall ein IA*. Ein zugriff auf den konstanten Container liefert mir ein const T&, also ein IA *const& - eine Referenz auf einen Konstanten zeiger auf ein (nicht-konstantes) IA
-
pumuckl schrieb:
Simon2 schrieb:
In der "Container-Theorie" schon (bei einem Array geht das vermutlich) ... aber Du bekommst bei einer const-map keine veränderbaren Referenzen auf die Elemente.
Oder habe ich da was übersehenJa, hast du. ...
Stimmt.
Das ist mir kurz nach meinem letzten Post aufgefallen, ich war allerdings nicht "am Platz". So gesehen ist das wirklich keine Lösung für hoijui ...
Gruß,
Simon2.
-
hoijui schrieb:
ist die idee eigentlich, dass der IA copy-ctor gar nie benutzt werden muss? soll ich desshalb alles mit pointern machen?
Das du mit Zeigern oder Smartpointern arbeiten sollst liegt nicht an den verhindern der Kopien, sondern das ein konkretes C++ Objekt auch genau zu dem Typ passen muss. Das du davon nichts in Java siehst hat mit einer meiner ersten Aussagen zu tun, das eigentlich alle Objektzugriffe in Java intern mit Zeigern realisiert werden.
Nehmen wir mal folgendes an:
struct A { int a; }; struct B : public A { int b; } int main() { A var1; // var1 enthält ein int (a) B var2; // var2 enthält zwei int (a, b) A var3 = var2; // var3 enthält ein int (a), der B-Teil wird abgeschnitten }Daher gehen nur Zeiger oder Referenzen, da diese nichts am Objektstatus ändern (Nur weil du über ein A-Zeiger auf B Zugreifst wird das B nicht zum A).
cu André
-
hoijui schrieb:
ist die idee eigentlich, dass der IA copy-ctor gar nie benutzt werden muss?
Der IA Copy-Ctor wird immer dann benutzt, wenn ein Copy-Ctor einer abgeleiteten klasse benutzt wird. Copy-Ctoren rufen normalerweise die Copy-Ctoren ihrer Basisklassen auf, da der Basisklassen-Teil ja auch konstruiert werden muss.
Anhand des Brötchebeispiels: Brötchen sind abstrakt (weil keiner ein trockenes brötchen ohne alles will), aber um ein Matjesbrötchen zu kopieren muss das Brötchen selbst mitkopiert werden:
class Broetchen //trocken und ohne alles { public: Broetchen(Broetchen const& rhs) { //do something } virtual ~Brötchen() = 0 {} }; class Matjesbroetchen : public Broetchen { Matjes dermatjes; public: Matjesbroetchen(Matjesbroetchen const& rhs) : Broetchen(rhs), dermatjes(rhs.matjes) {} };
-
im moment siehts also so aus:
class Manager { public: map<boost::smart_ptr<const IA>, boost::smart_ptr<const IB> > GetAssoc() { map<boost::smart_ptr<const IA>, boost::smart_ptr<const IB> > c_assoc; map<boost::smart_ptr<IA>, boost::smart_ptr<IB> >::const_iterator it; for (it=assoc.begin(); it!=assoc.end(); it++) { c_assoc[boost::smart_ptr<IA>(new CA(it->first))] = boost::smart_ptr<IB>(new CB(it->second)); } return c_assoc; } private: map<boost::smart_ptr<IA>, boost::smart_ptr<IB> > assoc; void InsertAssoc(const IA& a, const IB& b) { assoc[boost::smart_ptr<IA>(new CA(a))] = boost::smart_ptr<IB>(new CB(b)); } }muss sagen.. das sieht schon ziemlich haesslich aus, wird wohl bald fertig sein, hm?
sollt ich in IA und IB eine clone() methode haben, damit ich die anstatt den copy-ctor von CA und CB benutzen kann? waehre wohl schoener, auch wenn ich glaube es waere nicht notwendig fuer meine zwecke.
-
hoijui schrieb:
im moment siehts also so aus:
muss sagen.. das sieht schon ziemlich haesslich aus ...
Naja, Du hast Dir ja auch maximal Mühe gegeben, den Code häßlich zu machen.
Mit ein paar typedefs wäre das doch schon ziemlich entspannt.BTW: C-Style-Casts deprecated; sind die Upcasts wirklich nötig ?
... und Deine "Kopiersemantik" finde ich immer noch unglücklich/gefährlich. Du nutzt auch gar keinen Copy-Konstruktor (Quell- und Zieltyp identisch), sondern einen "normalen"....hoijui schrieb:
sollt ich in IA und IB eine clone() methode haben, damit ich die anstatt den copy-ctor von CA und CB benutzen kann? waehre wohl schoener, ...
Nur für "Javaisten, die an Althergebrachtem kleben".

Gruß,
Simon2.
-
Wisst ihr überhaupt was ein "abstrakter Datentyp" ist? Schlagt mal besser in einem Informatik-Handbuch/Lexikon nach.
-
in der for-schleife willst du ja smart-ptr auf const IA (bzw. const IB) in die ergebnis-map packen, solltest du also auch machen

class Manager { public: template <class T> struct sptr { typedef boost::smart_ptr<T> ptr; }; template <class T, class U> struct ptrmap { typedef std::map<sptr<T>::ptr, sptr<U>::ptr> map; typedef std::map<sptr<const T>::ptr, sptr<const U>::ptr> constmap; }; ptrmap<IA,IB>::constmap GetAssoc() { ptrmap<IA,IB>::constmap c_assoc(assoc.begin(), assoc.end()); // da die typen implizit konvertierbar sind, kann man auch den range-ctor nutzen :) return c_assoc; } private: ptrmap<IA,IB>::map assoc; void InsertAssoc(const IA& a, const IB& b) { assoc[sptr<IA>::ptr(new CA(a))] = sptr<IB>::ptr(new CB(b)); } }evtl müssen noch typename an der einen oder anderen Stelle eingebaut werden...
-
Hilfegebender schrieb:
Wisst ihr überhaupt was ein "abstrakter Datentyp" ist? Schlagt mal besser in einem Informatik-Handbuch/Lexikon nach.
in C/++ kann das vieles sein
ein definitiv nicht java konformes beispiel

typedef <setze typ ein> AbstrakterDatentyp; //<-hier passiert die abstraktion .-P ... AbstrakterDatentyp v1, v2; v1 = 10; //setzt implizite typenkonversion voraus v2 = v1; //setzt kopiersemantik voraus v1 = AbstrakterDatentyp(); //setzt default konstruktor voraus ...oder noch weniger java konform
[cpp]#define <insert datatype> AbstrakterTyp
AbstrakterTyp zuAbstrakt;
...
[cpp]scnr
-
danke pumuckl!

hab natuerlich noch nie was von nem range-ctor gehoert
aber wenns das so gibt und das funktioniert, gut
tja.. auch ohne den range-ctor haett ich da unnoetig objekte erzeugt.. sehr ich jetzt. danke auch da fuers aufzeigen.mir ist klar, dass ich bei euch C++ leutz da nicht auf zustimmung treffen werde, aber das ganze ist mit und ohne typedefs einfach sehr haesslich. obwohl es doch etwas so standardmaessiges ist, fuer OO: abstrakte datentypen in containern, mutable und unmutable. da C++ die OO variante von C ist, sollte OO damit einfach sein. dann wiederum.. dafuer gibts ja java

bei C++ kann man mer optimieren und bei Java ists dafuer einfacher.
ich dachte immer ich werd mich mit neuen sprachen beschaeftigen, das laege mir eigentlich eher.. jetzt begeb ich ich zurueck in der zeit.
ich mach das ganze uebrigens, weill ich an einem Java interface fuer eine C/C++ schnittstelle arbeite, damit ich mich nicht mehr mit solchen sachen rumschlagen muss.

-
hoijui schrieb:
...es doch etwas so standardmaessiges ist, fuer OO: abstrakte datentypen in containern, mutable und unmutable. da C++ die OO variante von C ist, sollte OO damit einfach sein. dann wiederum.. dafuer gibts ja java

bei C++ kann man mer optimieren und bei Java ists dafuer einfacher....Naja, Java hat halt deutlich geringere "const"-Möglichkeiten und eine deutlich schwächeres "Compiletime-Typsicherheit"... da werden diese Schwierigkeiten eben in die Laufzeit verlagert - wer geduldige Kunden hat, den stört das halt nicht.

Gruß,
Simon2.
-
Also wen man deine zwei argumente zu einem einzigen zusammenfasst, versteh ich was du meinst. Bei C++ gehoert (un-/)mutable zum datentyp in der compile time. in Java normalerweise nicht; das kan vorteile haben, z.B. muss man desshalb eine map nicht umkopieren, sondern kann sie einfach mit einer anderen map wrappen, wenn dies logisch moeglich ist. diese map koennte z.B. UnmutableMap heissen und ein leeres marker interface Unmutable oder eine Annotation Unmutable enthalten, so kriegt man auch compiletime mutable checks (und der code ist immernoch viel simpler ;-)).
Ich seh ja viele vorteile in C++, vonwegen mehr kontrolle, dass man genau sehen kann was abgeht (pointer, referenzen, values z.B.), dass man mehr fine-tuning machen kann, ...
aber dass die Kunden laenger auf stabilen code warten muessen, das scheint mir eine ziemlich unrealistische annahme.uebrigens.. wie wuerde ich nun den copy konstruktor von IA implementieren? ich hab bis jetzt imer nur gehoert wies nicht geht.
-
hoijui schrieb:
Bei C++ gehoert (un-/)mutable zum datentyp in der compile time. in Java normalerweise nicht; das kan vorteile haben, z.B. muss man desshalb eine map nicht umkopieren, sondern kann sie einfach mit einer anderen map wrappen, wenn dies logisch moeglich ist. diese map koennte z.B. UnmutableMap heissen und ein leeres marker interface Unmutable oder eine Annotation Unmutable enthalten, so kriegt man auch compiletime mutable checks (und der code ist immernoch viel simpler ;-)).
Wobei genau siehst du den Vorteil von
UnmutableMap constMap = new UnmutableMap(mutableMap);
gegenüber:
Map const& constMap = mutableMap;
immutable ist ein konzept dass man braucht wenn man kein const in der Sprache hat. In C++ ist jeder Datentyp mutable oder immutable und du kannst dir aussuchen wann er was ist. dh auch, dass man handles auf interne Daten zurück geben kann - weil man sie immutable zurück geben kann - wo man in Java entweder eine wrapper Klasse braucht oder eine defensive kopie anlegen muss...
-
ich meinte eher den unterschied zwischen dem:
UnmutableMap<IA, IB> um;und dem:
template <class T> struct sptr { typedef boost::smart_ptr<T> ptr; }; template <class T, class U> struct ptrmap { typedef std::map<sptr<T>::ptr, sptr<U>::ptr> map; typedef std::map<sptr<const T>::ptr, sptr<const U>::ptr> constmap; }; ptrmap<IA,IB>::map um;Ich hab schon wieder ein problem:
class Manager { public: template <class T> struct sptr { typedef boost::smart_ptr<T> ptr; }; template <class T, class U> struct ptrmap { typedef std::map<sptr<T>::ptr, sptr<U>::ptr> map; typedef std::map<sptr<const T>::ptr, sptr<const U>::ptr> constmap; }; ptrmap<IA,IB>::constmap GetAssoc() { ptrmap<IA,IB>::constmap c_assoc(assoc.begin(), assoc.end()); // da die typen implizit konvertierbar sind, kann man auch den range-ctor nutzen :) return c_assoc; } sptr<const IB>::ptr Find(const sptr<const IA>::ptr& a) const { return assoc.find(a); // compiler error: invalid conversion from 'const IA* const' to 'IA*' } private: ptrmap<IA,IB>::map assoc; void InsertAssoc(const IA& a, const IB& b) { assoc[sptr<IA>::ptr(new CA(a))] = sptr<IB>::ptr(new CB(b)); } }bei der Find() methode. Das uebergebene IA ist const, aber weill die map smart pointers auf nicht const IA als key benutzt, kann ich mit einem IA nicht in der map suchen. ich will die IAs in meiner map aber nicht const haben, und ich will in der find methode garantieren dass das uebergebene IA nicht veraendert wird. das waere ja auch nicht der fall, aber der compiler laesst mich nicht. wie soll ich das tun?