Gibt es sowas wie <?> unter C++?
-
Ich moechte kein new im Konstruktor haben, damit die Werte moeglichst auf dem Stack abgelegt werden. Im Moment habe ich die new-Version und das ist einfach zu langsam (da extrem viele dieser Polygone instanziiert werden).
Der Grund warum ich nun auf die Template-Loesung will ist der folgende: Von Polygon habe ich eine Klasse namens Quad abgeleitet, und dieses ruft einfach den Polygon-Konstruktor mit dem int-Parameter 4 auf. Also wird die Groesse auf 4 gesetzt und ein Array in dieser Groesse erstellt. Nun wollte ich das wie gesagt aendern, um nicht mehr so viele news zu haben, und habe zwei Moeglichkeiten: Entweder ich leite Quad nicht mehr von Polygon ab, sondern vielleicht beide Klassen von einem gemeinsamen Interface, oder ich leite Quad von Polygon ab, aber verwende ein Template fuer Polygon, wo die Groesse direkt mitgegeben wird.
Im Übrigen ist C++ eben nicht Java, sondern hat seine eigenen Programmierparadigmen. Also sollte man auch nicht versuchen etwas "so wie in Java" zu realisieren.
In Java geht es nichtmal, Zahlen als Template-Argumente zu verwenden, also habe ich auch nicht vor, es "so wie in Java" zu machen. Ich weiss nur dass man in Java generalisieren kann, indem man statt eindeutiger Template-Argumente ein Fragezeichen angibt, und meine Frage war nur, ob es eine vergleichbare Moeglichkeit auch in C++ gibt.
EDIT: Ich vergass zu erwaehnen, dass gerade die Quads wie bloed instanziiert werden. Genau DORT ist es mir zu langsam. Aber ich faend es halt nach wie vor huebsch, wenn Quad von Polygon abgeleitet waere.
-
Du kannst eine Interface IPolygon definieren, von dem Quad erbt und die entsprechenden Methoden implementiert. Dabei ist die Arraygroesse der Punkte fest a la Point3D points[4]. Andere Moeglichkeiten: eigene Speicherverwaltung, Nachnutzung von Objekten aus einem Pool, ...
-
Von Polygon habe ich eine Klasse namens Quad abgeleitet
Welche Funktionalitaet hat denn "Quad" die "Polygon" nicht schon beinhaltet?
da extrem viele dieser Polygone instanziiert werden
Haeufig haben unterschiedliche Polygone gemeinsame Eckpunkte sodass auch ein gemeinsamer Pool von Eckpunkten Sinn macht und eine Allozierung pro Element entfaellt.
-
Quad ist in dem Fall eher als eine Art typedef anzusehen und ruft bisher einfach nur Polygon(4) auf. Zudem kann ich z.B. bei Vergleichsoperatoren einfach alle 4 Punkte vergleichen, ohne eine Schleife zu machen (wie es bei einem beliebig grossen Polygon der Fall waere).
Das mit den gemeinsamen Eckpunkten kommt in meinem Anwendungsfall kaum vor, da handelt es sich eher um Polygone, die sich ueberlappen.
EDIT: Uebrigens werden auch die anderen Polygone spaeter nie in ihrer Groesse veraendert, daher waere das mit Templates halt schon praktisch. Was mich gerade wundert: ich kann in der Quad-Klasse das points-Array einfach nochmal neu definieren, aber was laeuft dann eigentlich ab, wenn die Polygon-Methoden aufgerufen werden? Ist das dann einfach in Polygon::points und Quad::points unterteilt?
-
Generics != Templates
Die Syntax mit den <>-Klammern ist zwar gleich, aber es passiert doch letztendlich was anderes. Generics werden zwar vom Compiler überprüft, aber der erzeugte Code ist wieder die
java.lang.Object.
-
Rock Lobster schrieb:
Servus,
einige Java-Programmierer kennen sicherlich das <?>-Konstrukt, um quasi zu sagen, dass einem die Typparameter wurscht sind.Gibt es sowas auch unter C++?
Nein. "Java Generics" ist etwas völlig anderes. Die Typparameter einer generischen Klasse gehören nicht wirklich zum Typ der Klasse ("Type Erasure"). Sie werden nur dazu benutzt, zur Compilezeit etwas mehr Typechecking durchzuführen und dem Nutzer das Typecasting abzunehmen. Das ganze funktioniert wirklich nur wegen der Indirektion (Objekte kann man nie "direkt halten" sondern nur über Referenzen erreichen) und über Laufzeitpolymorphie.
In C++ könnte man das so machen:
class abstraktes_poly { public: virtual ~abstraktes_poly() {} virtual abstraktes_poly* clone() const = 0; virtual int ecken() const = 0; virtual punkt ecke(int index) const = 0; }; template<int Ecken> class konkretes_poly : public abstraktes_poly { punkt m_ecken[Ecken]; public: ...Füge Code für Initialisierung ein... konkretes_poly<Ecken>* clone() const {return new poly<Ecken>(*this);} int ecken() const {return Ecken;} punkt ecke(int index) const {return m_ecken[index];} } int main() { auto_ptr<abstraktes_poly> apap1 ( new konkretes_poly<4> ); auto_ptr<abstraktes_poly> apap2 ( new konkretes_poly<5> ); cout << apap1->ecken(); cout << apap2->ecken(); }Statt auto_ptr kann man diese Zeiger natürlich auch in einem Objekt einer eigens dafür geschriebenen Klasse verwalten. Dadurch kann man auch Wert-Semantik emulieren. Das ganze nennt sich dann auch "C++ Type Erasure" und wird u.a. bei Boost.Function und Boost.Any angewendet.
Gruß,
SP
-
Hallo Rock Lobster,
Rock Lobster schrieb:
Ich moechte kein new im Konstruktor haben, damit die Werte moeglichst auf dem Stack abgelegt werden. Im Moment habe ich die new-Version und das ist einfach zu langsam (da extrem viele dieser Polygone instanziiert werden).
Ich frage mich gerade wie du so viele Objekte auf dem Stack ablegst.
(Rekursion schliesse ich einfach mal aus)
Wo verwaltest du denn die ganzen Polygone? Kurz gesagt, ich befürchte dass
du entweder jedesmal den Copy-Konstruktor aufrufst oder auf identische
Speicherbereiche verweist.Gruß,
CSpille
-
Also in einer wichtigen Initialisierungsroutine am Anfang des Programms wird erstmal ein Haufen Quads in einen std::vector gespeichert (und zwar direkt als Objekt, nicht als Pointer). Da waere es jetzt halt unguenstig, wenn pro Quad im Konstruktor ein new aufgerufen wird, da das ja den gesamten Vorgang sehr ausbremsen kann. Also ist es mir lieber, wenn die Arrays von vorneherein initialisiert werden.
Ich werde nun aber ueberlegen ob ich Polygon und Quad nicht einfach getrennt als Klasse haben soll. Die Anzahl der Methoden und Operatoren haelt sich sehr stark in Grenzen, und es ist wahrscheinlich auch nicht notwendig, dass sie ineinander gecasted werden muessen, von daher ist es vielleicht wirklich die sinnvollste Loesung, einfach zwei unterschiedliche Klassen zu verwenden. Das urspruengliche Design ging halt von schoenem OOP aus, aber jetzt ist die Geschwindigkeit deutlich wichtiger als das Klassendesign.
-
am Anfang des Programms ... im Konstruktor ein new aufgerufen wird, da das ja den gesamten Vorgang sehr ausbremsen kann
Omg. Das passiert am Anfang und nur ein Mal. D.h. es spielt fuer das restliche Programm keine Rolle? Und dieser Konjunktiv stoert mich. Hast du man getestet und nachgemessen, ob es ueberhaupt ein Performanceloch ist?
ueberlegen ob ich Polygon und Quad nicht einfach getrennt
Haeh, hast du dir die Posts nicht durchgelesen. Du kannst OOP und Geschwindigkeit haben. Warum stellst du erst die Frage und machst es dann doch ganz anders. Beratungsresistent ...
-
Rock Lobster schrieb:
...Nun moechte ich aber von manchen Funktionen aus ein Polygon beliebiger Groesse zurueckgeben, ...
Warum nicht wieder ein Funktionstemplate? Dann muss allerdings der Aufrufer die Dimension schon wissen (und sei es nur implizit).
template <int DIM> Polygon<DIM>* f(Polygon<DIM> const& dummy); template <int DIM> Polygon<DIM>* f(int DIM) { return f(Polygon<DIM>); }Gruß,
Simon2.
-
Ich haette vielleicht nicht "Programm" sondern "Vorgang" sagen sollen. Dieser Vorgang dauert im ersten Schritt sehr lang, es kommen danach noch ein paar Schritte die weniger lange dauern. Und der Vorgang kommt in meinem jetzigen "Programm" nur einmal vor, aber spaeter soll er immer wieder vorkommen koennen.
Nachgemessen habe ich auch. Es kommen Daten uebers Netzwerk, und die CPU ist auf Voll-Last, das Netzwerk aber nicht. Also ist wohl davon auszugehen, dass in meinem Code zu viel passiert, und aus diesem Grund waere es sinnvoll, dort ein paar Dinge zu aendern. Da nun in jedem einzelnen Quad-Konstruktor jedoch ein unnoetiges "new" gemacht wird, waere das zumindest mal ein erster Anhaltspunkt.
Ich habe mich gerade mit einem Kollegen unterhalten und ihm auch die Version mit der gemeinsamen IPolygon-Basisklasse vorgeschlagen (hatte sogar schon angefangen zu implementieren), er meinte jedoch, ich soll auf OOP in dem Fall verzichten, weil durch virtuelle Funktionen auch wieder unnoetig Overhead entsteht, der bei solch einer simplen Extrem-Basis-Klasse wie es ein Quad nunmal ist einfach nicht sein muss. Das war der Grund, warum ich sagte, dass ich nun wohl doch lieber eine voellig eigene, OOP-freie Implementierung einer Quad-Klasse bevorzugen wuerde. Hat aber nix mit beratungsresistent zu tun.
-
@ Simon2: Vielen Dank, auf diese Idee bin ich bisher nicht gekommen. Jedoch denke ich, dass auch das in meinem Anwendungsfall nicht sinnvoll sein wird (da man vorher nicht weiss, wie viele Punkte das Polygon dann haben wird). Mir ist nun auch klar warum das mit Templates nicht funktionieren kann. Trotzdem danke fuer den Hinweis, vielleicht kann ich das mal an einer anderen Stelle gut gebrauchen.
-
CPU ... Volllast ... Netzwerk nicht ... erster Anhaltspunkt ...
Mit Nachmessen meine ich eine Profiler zu benutzen und genau zu schauen, wo viel Rechenleistung verbraten wird und nicht aufs geradewohl irgendwas zu optimieren.
Quad-Konstruktor jedoch ein unnoetiges "new"
Und es wurde gezeigt, wie das zu beheben ist, mit OOP. Ausserdem waere dann ein Objektpool wahrscheinlich angebrachter.
ich soll auf OOP in dem Fall verzichten, weil durch virtuelle Funktionen auch wieder unnoetig Overhead entsteht
in jedem Fall ... Hast du ihn auch gefragt, aus was der Overhead besteht und wieviel Zeit er kosten wird? Es ist eine zusaetzlich Dereferenzierung eines Zeigers. Also verdammt billig.
Ich empfehle dir die Buecher von Scott Meyers: "Effektiv C++ programmieren" und "Mehr effektiv C++ programmieren".
-
Simon2 schrieb:
template <int DIM> Polygon<DIM>* f(Polygon<DIM> const& dummy); template <int DIM> Polygon<DIM>* f(int DIM) { return f(Polygon<DIM>); }Ich bezweifle dass das funktioniert, denn erstens hast du für zwei verschiedene Identifyer denselben Bezeichner gewählt (einmal den Templateparameter, einmal den Funktionsparameter), zweitens wird erst zur Laufzeit entschieden, welches Argument der Funktion übergeben wird, der Templateparameter muss aber schon zur Laufzeit feststehen.
Das Ganze müsste daher eher mit einer Art Int2Type als Funktionsargument geschehen:template <int DIM> Polygon<DIM>* f(Int2Type<DIM> /*dummy*/);
-
pumuckl schrieb:
...Ich bezweifle dass das funktioniert, ...
Sollte auch eher eine Ideenskizze sein (hätte ich wohl besser hingeschrieben).
Außerdem war die Idee zur 2. Funktion eher ein "Spontanblitz" .... was wohl (wie so oft) unter die Kategorie "Der Erste aber nicht der Beste" fällt
Deine Kritikpunkte zielen im Wesentlichen auf mein zweites template ab, oder?...
Gruß,
Simon2.
-
Rock Lobster schrieb:
...die CPU ist auf Voll-Last...
Das ist an sich ein gutes Zeichen.
Die Frage ist eher, ob der Prozess insgesamt zu lange dauert ... und ob Deine ergriffenen Maßnahmen helfen.Gruß,
Simon2.
-
Simon2 schrieb:
Deine Kritikpunkte zielen im Wesentlichen auf mein zweites template ab, oder?...
Jup.
@Rock: wie schon gesagt wurde, und wie du auch richtig erkannt hast gibts zwei Möglichkeiten: die "Größe" (= Zahl der Ecken) jedes Polygons zur Compilezeit festlegen -> alles mit Templates, oder zur Laufzeit -> keine Templates, dafür dynamische Speicherverwaltung.
-
pumuckl schrieb:
...zwei Möglichkeiten: die "Größe" (= Zahl der Ecken) jedes Polygons zur Compilezeit festlegen -> alles mit Templates, oder zur Laufzeit -> keine Templates, dafür dynamische Speicherverwaltung.
Plus die "Frickellösungen" (die bisweilen tatsächlich optimal sein können); z.B.:
- eigene Speicherverwaltung
- "feste templates"(Ideenskizze !!!)
template <int DIM> struct Polygon { }; template <typename T> T f(T const& dummy){ ... } int main() { //... if(irgendeinKriterium) { vector<Polygon<4> > myPolys(1000); myPoly[2] = f(myPoly[0]); } else { vector<Polygon<16> > myPolys(1000); myPoly[2] = f(myPoly[0]); }Nicht unbedingt schön und auch nur für kleine konkrete Zahlen anzuraten, aber bei recht aufwendigem f() kann das schon das geringere Übel sein....
Gruß,
Simon2
-
Rock Lobster schrieb:
Ich moechte kein new im Konstruktor haben, damit die Werte moeglichst auf dem Stack abgelegt werden. Im Moment habe ich die new-Version und das ist einfach zu langsam (da extrem viele dieser Polygone instanziiert werden).
Bist du sicher, dass das am
newalleine liegt? Hat dir dein Profiler gesagt, dass die Funktionoperator newden kritischen Teil der Zeit beansprucht?Ich würde eher raten, eine eigene Speicherverwaltung einzurichten, was in diesem Thread auch schon mehrmals erwähnt wurde. Dadurch kannst du dir vielen Speicher aufs Mal anfordern und den dann nach und nach verteilen. Aber der Stack eignet sich nicht für extrem viele oder grosse Objekte.
-
Hallo Christian,
gib uns mal etwas mehr Code. Ich kann mir beim besten Willen nicht vorstellen,
dass das wirklich das Problem in deinem Programm ist. Über wieviele Objekte
innerhalb welches Zeitraums unterhalten wir uns denn?Gruß,
CSpille