Konsolen-Simulation des Algorithmus "Türme von Hanoi" unter Verwendung einer Klasse
-
SeppJ schrieb:
Die Trennung von Logik und Darstellung ist eher ein technischer Aspekt. Man möchte die Darstellung abkoppeln, damit sie austauschbar wird. So kann die gleiche Programmlogik auf allerlei Art dargestellt werden. Dann wäre es ein leichtes, mein Progrämmchen mit einer anspruchsvollen 3D-Grafik auszurüsten, ohne an der Logik irgendetwas verändern zu müssen.
..
Bei Werners Code? Die gehören mit zum Namen und haben keine syntaktische Bedeutung. Werner will dadurch unterscheidbar machen, ob eine Variable lokal ist oder ein Klassenmember ist.Achso, danke. Dann wäre man gut beraten, es so zu designern, dass die Windows-Programmierung später ohne Probleme anknüpfen kann
-
osdt schrieb:
Bist Du so freundlich uns zu erklären weshalb Du X(...) und Y(...) als globale Funktionen implementierst? Ich hab das hier schonmal irgendwo entdeckt und nur mit dem Kopf geschüttelt
Du weißt schon dass es dafür Klassenmethoden gibt?
Nun - sehe als persönlichen Stil meinerseits. Vom Standpunkt des Designs ist es IMHO völlig egal, ob Du in einer Klasse eine Methode oder eine (friend) Funktion, mit der Klasse als einer der Parameter implementierst. Technisch gibt es da Feinheiten - die kannst Du bei Scott Meyers nachlesen.
In diesem speziellen Fall bei Positionen habe ich mir das so angewöhnt, und als ich es oben geschrieben habe, war das reine Gewohnheit ohne konkret darüber nach zu denken. Es hat nämlich den Vorteil, dass man sehr viele Strukturen als Positionen verwenden kann. Teilweise solche, wo man keine Methoden hinzufügen kann, weil es sich um sogenannten Legacy Code oder um eine Klasse aus dem Standard wie std::pair<> oder std::complex<> handelt.
Man kann aber immer diese Funktionen X() und Y() implementieren. Das ist ein großer Vorteil, insbesondere bei der Template-Programmierung.osdt schrieb:
Daraus resultiert dann völlig unleserlicher Code wie:
for( int x = X(d.lower_left_); x <= X(d.upper_right_); ++x )
.. erstaunlich! Denke Dir mal X(egal) heißt X-Wert-von-egal. Dann steht da:
for( int x = X_Wert_von(d.lower_left_); x <= X_Wert_von(d.upper_right_); ++x )
also: Schleife über alle x beginnend vom X-Wert der (unteren) linken Ecke bis zum X-Wert der (oberen) rechten Ecke.
Gruß
Werner
-
Hallo SeppJ,
wenn dieses Forum eine Person wäre, müsste es wegen Schizophrenie sofort zum Psychiater.
SeppJ schrieb:
.. die Objekte zeichnen sich selbst, das sollte nicht so sein. Logik und Darstellung sollten sauber getrennt sein, aber ich kenne die üblichen Designs dafür nicht und so wirklich zufrieden war ich auch nicht mit dem, was ich mir auf die Schnelle ausgedacht habe. Dein Design ist da ähnlich, wenn auch eleganter. Ist das schon eine der üblichen Vorgehensweisen? Da müsste man jetzt wohl wirklich mal einen Spieleprogrammierer fragen, wie das gut gemacht wird.
ich hätte mir diese Diskussion - und ich begrüße sie sehr
- eher in diesem Thread gewünscht, wo ich explizit danach fragte, wie man Code verständlich schreiben kann. Fünf von fünf die geantwortet haben, waren der Meinung, dass der Code des Springerproblems durch meinen (IMHO ojektorientierten) Ansatz nur komplexer, länger und weniger verständlich wird.
Und dann poste ich hier ein Schnipsel im gleichen Stil und Du meinst:SeppJ schrieb:
Logik und Darstellung sollten sauber getrennt sein
- also noch mehr Code und noch mehr Klassen und womöglich noch weniger verständlich?
Ja - natürlich sollte man Logik und Darstellung sauber trennen - was denn sonst. In sogenannten 'professionellen' Programmen kenne ich das gar nicht mehr anders. Und das machen nicht nur Spieleprogrammierer so - Modell-View-Architekturen machen auch wo anders Sinn. Und sie machen auch Sinn, wenn man die View nicht austauschen will. Einfach um die Komplexität runter zu kriegen.
Ob man das bei den Türmen von Hanoi macht oder nicht ist was anderes. Der Turm ist viel zu einfach, als dass das wirklich nenneswerte Verbesserungen des Programms nach sich zieht. Adererseits ist das 'ne nette Übung - und wenn man es mit dem Turm aus Hanoi nicht sauber hinbekommt, braucht man nicht zu hoffen, dass es bei großen/komplexen Objekten einfacher wird.
ich melde mich zu dem Thema später noch mal.
Gruß
Werner
-
Ufff ... Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...
http://s14.directupload.net/images/131208/5afa74r2.jpg
Soweit sind die Klassen fertig designed u. programmiert. Es scheint auch zu funktionieren. Ob ich gravierende Verstöße gegen die objektorientierte Programmierung gemacht habe, weiß ich allerdings nicht:
#include <windows.h> // wegen Windows Handle für Konsolenposition #include <cstdlib> // wegen system("cls") #include <sstream> // wegen stringstream, für int kombiniert mit char* #include <iostream> #include <deque> #include <string> using namespace std; class Slice { public: Slice(); ~Slice(); void setSlice(string slice); string getSlice(); int getSliceCenter(); int getSliceSize(); private: string m_slice_object; int m_slice_length; int m_slice_center; }; Slice::Slice() { } Slice::~Slice() { } //--------------------------------------------------------------------- // gibt der Scheibe ein Aussehen //--------------------------------------------------------------------- void Slice::setSlice(string slice) { m_slice_object = slice; } //--------------------------------------------------------------------- // holt die Scheibe //--------------------------------------------------------------------- string Slice::getSlice() { return m_slice_object; } //--------------------------------------------------------------------- // bestimmt die Mitte der Scheibe //--------------------------------------------------------------------- int Slice::getSliceCenter() { m_slice_center = (m_slice_object.length()+1)/2; return m_slice_center; } //--------------------------------------------------------------------- // bestimmt die Größe der Scheibe //--------------------------------------------------------------------- int Slice::getSliceSize() { m_slice_length = m_slice_object.size(); return m_slice_length; } class Tower { public: Tower(); ~Tower(); Slice getSlice(); //oberste Scheibe zurückgeben Slice getAt(const int &iter); // irgendwo eine Scheibe int getSize(); // Towergröße auslesen void addSlice(Slice slice); // 1x aufnehmen void removeSlice(); // 1x löschen private: deque<Slice> m_tower_content; }; Tower::Tower() { } Tower::~Tower() { } //--------------------------------------------------------------------- // holt oberste Scheibe //--------------------------------------------------------------------- Slice Tower::getSlice() { return m_tower_content.at(0); } //--------------------------------------------------------------------- // holt eine Scheibe an einer best. Stelle im Turm //--------------------------------------------------------------------- Slice Tower::getAt(const int &iter) { return m_tower_content.at(iter); } //--------------------------------------------------------------------- // Scheibenzahl auslesen //--------------------------------------------------------------------- int Tower::getSize() { return m_tower_content.size(); } //--------------------------------------------------------------------- // fügt eine Scheibe oben rein //--------------------------------------------------------------------- void Tower::addSlice(Slice slice) { m_tower_content.push_front(slice); } //--------------------------------------------------------------------- // löscht oberste Scheibe aus Turm //--------------------------------------------------------------------- void Tower::removeSlice() { m_tower_content.pop_front(); } class Display { public: Display(); ~Display(); void clr(); void gotoXY(const int &line, const int &column); void waitKeyboard() {getwchar();} void print(const string &value); void print(const stringstream &value); void print(Tower &tower, int x_start, const int &y_start); }; Display::Display() { } Display::~Display() { } //--------------------------------------------------------------------- // // Bildschirm löschen //--------------------------------------------------------------------- void Display::clr() { system("cls"); } //--------------------------------------------------------------------- // Positioniere Cursor auf Konsole //--------------------------------------------------------------------- void Display::gotoXY(const int &line, const int &column) { COORD p; p.Y = line; // Zeile p.X = column; // Spalte SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p); } //--------------------------------------------------------------------- // Überladung 1: String auf dem Bildschirm //--------------------------------------------------------------------- void Display::print(const string &value){ cout << value << endl; } //--------------------------------------------------------------------- // Überladung 2: StringStreams auf dem Bildschirm //--------------------------------------------------------------------- void Display::print(const stringstream &value){ cout << value.str() << endl; } //--------------------------------------------------------------------- // Überladung 3: Tower auf Konsole ausgeben //--------------------------------------------------------------------- void Display::print(Tower &tower, int x_start, const int &y_start) { int start_column; // Startspalte for (int i=tower.getSize()-1; i > -1; i--) { start_column = y_start - tower.getAt(i).getSliceCenter(); this->gotoXY(x_start--, start_column); cout << tower.getAt(i).getSlice(); } } class Simulation { public: Simulation(); ~Simulation(); void moveSlice(Tower &start_tower, Tower &destination_tower); void recurFunction(int start, int destination, int slice_count, Tower &tower_1, Tower &tower_2, Tower &tower_3); private: int m_counter; // Schrittzähler }; Simulation::Simulation() { m_counter = 0; } Simulation::~Simulation() { } //--------------------------------------------------------------------- // Scheibe von Turm zu Turm bewegen //--------------------------------------------------------------------- void Simulation::moveSlice(Tower &start_tower, Tower &destination_tower) { destination_tower.addSlice(start_tower.getSlice()); start_tower.removeSlice(); } //------------------------------------------------------------------------- // Rekursiver Methodenaufruf zur Berechnung & Anzeige der Möglichkeiten //------------------------------------------------------------------------- void Simulation::recurFunction(int start, // Startturm int destination, // Zielturm int slice_count, // Anzahl Scheiben Tower &tower_1, Tower &tower_2, Tower &tower_3) { Display console; // Konsole erstellen stringstream step_no; // Schrittnummer int free; // freier Turm if (slice_count != 0) { free = 6-start-destination; // rekursiver Aufruf recurFunction(start, free, slice_count-1, tower_1, tower_2, tower_3); // Schritte anzeigen console.gotoXY(0,0); step_no << ++m_counter << ".) Turm " << start << " nach -> Turm " << destination << endl; console.print(step_no); console.gotoXY(1,26); // Ausgangszustand des Turms 1malig anzeigen if (m_counter == 1) { console.print(tower_1, 9, 15); console.gotoXY(1,26); } console.waitKeyboard(); // Simulationsteil: if (start == 1 && destination == 2) moveSlice(tower_1, tower_2); // Tower1->Tower2 if (start == 1 && destination == 3) moveSlice(tower_1, tower_3); // Tower1->Tower3 if (start == 2 && destination == 1) moveSlice(tower_2, tower_1); // Tower2->Tower1 if (start == 2 && destination == 3) moveSlice(tower_2, tower_3); // Tower2->Tower3 if (start == 3 && destination == 1) moveSlice(tower_3, tower_1); // Tower3->Tower1 if (start == 3 && destination == 2) moveSlice(tower_3, tower_2); // Tower3->Tower2 // neu zeichnen console.clr(); console.print(tower_1, 9, 15); console.print(tower_2, 9, 42); console.print(tower_3, 9, 68); // erneuter rekursiver Aufruf recurFunction(free, destination, slice_count-1, tower_1, tower_2, tower_3); } console.gotoXY(15,0); } //--------------------------------------------------------------------- // Programm-Hauptfunktion //--------------------------------------------------------------------- int main() { Slice slice[6]; // 6 Scheiben mit folgendem Aussehen slice[0].setSlice( "***"); slice[1].setSlice( "*******"); slice[2].setSlice( "***********"); slice[3].setSlice( "***************"); slice[4].setSlice( "*******************"); slice[5].setSlice("***********************"); // 3 Türme erstellen Tower tower_1, tower_2, tower_3; // Turm_1 mit Scheiben auffüllen for (int i = 5; i > -1; --i) { tower_1.addSlice(slice[i]); } // Simulation starten Simulation sim; sim.recurFunction(1, 3, 6, tower_1, tower_2, tower_3); return 0; }
-
-
Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...
Was!?
-
SeppJ schrieb:
Meinst du die syntaktische Deklaration der Methoden ... dass sie so ungefährt deklariert werden sollten?
-
Arcoth schrieb:
Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...
Was!?
Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM
Muss mir zu Weihnachten mal was Neues göhnen.
-
Ach, du redest von der IDE... ich dachte, du redest von der eigentlichen ausführbaren Datei.
-
Cpp_Anfaeger schrieb:
Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM
Muss mir zu Weihnachten mal was Neues göhnen.
Ja, z.B. Linux.
-
göhner schrieb:
Cpp_Anfaeger schrieb:
Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM
Muss mir zu Weihnachten mal was Neues göhnen.
Ja, z.B. Linux.
Ja, wurde mir auch schon empfohlen. Ubuntu glaube ich war das ... soll ziemlich sehr klein sein.
-
Cpp_Anfaeger schrieb:
Ufff ... Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...
http://s14.directupload.net/images/131208/5afa74r2.jpg
Soweit sind die Klassen fertig designed u. programmiert. Es scheint auch zu funktionieren.
Klassen sind schon umfangreicher, aber man gewinnt Übersicht, wenn man es richtig anstellt. Mehr Speicher brauchen sie deshalb nicht.
So umfangreich, wie Du es gemacht hast, brauchen sie auch gar nicht zu sein. Versuche nicht alles zwanghaft in eine Klasse zu packen. SeppJ hat Dir mit dem Link schon den Tipp gegeben, dass Du zumindest den Destruktor überall einsparen kannst. Der wird automatisch richtig erzeugt, solange Du einfache Member hast und nicht etwa Pointer mit allokiertem Memory.
Die Klasse Simulation ist wahrscheinlich völlig überflüssig, da reicht eine Funktion. Bei der Klasse Display sind zumindest die print-Funktionen überflüssig; hier würde gleich eine Ausgabe auf std::cout reichen.
Schau mal wasDisplay
sonst noch macht und was die MethodeSimulation::recurFunction
macht. In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen, wenn man den String für
Slice
gleich im Konstruktor mitgibt. Tipp: ein Objekt sollte 'gebrauchsfertig' sein, wenn der Konstruktor verlassen wird. Das spart dann auch gleich die MethodeSlice::setSlice
ein. So sähe es dann z.B. aus:// 3 Türme erstellen Tower tower_1, tower_2, tower_3; // Turm_1 mit Scheiben auffüllen string bild = "***********************"; for (int i = 5; i > -1; --i) { tower_1.addSlice(Slice(bild)); if( i > 0 ) bild.erase( bild.end()-4, bild.end() ); }
Wenn man eine Klasse entwirft, sollte man sich zunächst darüber Gedanken machen, was die Klasse repräsentiert, welche Verantwortlichkeit sie hat und wie sie vom Anwender benutzt wird. Die Internas spielen zunächst keine Rolle.
Beispiel Turm: Er repräsentiert den Stapel Scheiben, hält die Scheiben. Man muss eine Scheibe drauf legen und entfernen können - oder eine Scheibe von einem Turm zum anderen verschieben können. Es greift der Verschiebe-Algorithmus darauf zu und die Darstellung des Turms (Turm-View).
Dein Turm:class Tower { public: Tower(); // ~Tower(); // muss nicht // -- Schnittstelle zum Verschiebe-Algorithmus void addSlice(Slice slice); // 1x aufnehmen Slice getSlice(); //oberste Scheibe zurückgeben void removeSlice(); // 1x löschen // -- Schnittstelle zur Darstellung (View) Slice getAt(const int &iter); // irgendwo eine Scheibe int getSize(); // Towergröße auslesen // Member };
das sieht doch schon mal sehr sauber aus. Nur weiter so - Übung macht den Meister
Gruß
Werner
-
Danke dir für die guten Tipps zur Gestaltung der Klassen!
Werner Salomon schrieb:
SeppJ hat Dir mit dem Link schon den Tipp gegeben, dass Du zumindest den Destruktor überall einsparen kannst. Der wird automatisch richtig erzeugt, solange Du einfache Member hast und nicht etwa Pointer mit allokiertem Memory.
Achso, deswegen hat der Designer von Qt Creator standardmäßig keine Destruktoren in die Vorlagen eingeblendet. Ich hatte schon gedacht, dass das ein Bug wäre
Werner Salomon schrieb:
In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.
Ich habe versucht, die Klasse Display so gut wie möglich losgelöst zu erstellen. Inwieweit muss ich sie ändern, wenn ich in der Klasse Simulation etwas ändere? Ich habe gedacht, die Klasse Display gibt die Gebrauchsanweisung vor, wie sie zu benutzen ist, und daran muss jeder sich halten, der sie benutzen möchte.
Werner Salomon schrieb:
Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen
Ja, ich habe den Hang, alles so zu programmieren, damit ich sofort sehe, was vor sich geht. Dass da wiederum mehr Zeilen herauskommen ... super! Diese Technik ist wirklich neu für mich
:
// 3 Türme erstellen Tower tower_1, tower_2, tower_3; // Turm_1 mit Scheiben auffüllen string bild = "***********************"; for (int i = 5; i > -1; --i) { tower_1.addSlice(Slice(bild)); if( i > 0 ) bild.erase( bild.end()-4, bild.end() ); }
-
Cpp_Anfaeger schrieb:
Werner Salomon schrieb:
In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.
Ich habe versucht, die Klasse Display so gut wie möglich losgelöst zu erstellen. Inwieweit muss ich sie ändern, wenn ich in der Klasse Simulation etwas ändere? Ich habe gedacht, die Klasse Display gibt die Gebrauchsanweisung vor, wie sie zu benutzen ist, und daran muss jeder sich halten, der sie benutzen möchte.
ich meinte gar nicht
Display
sondern das main-Programm und die Simulation. In Der MethodeDisplay::gotoXY()
hast Du X und Y vertauscht - das verwirrt natürlich. X wird zur Zeilennummer (nach unten ansteigend) und Y ist die Spalte.Die Positionen in
Simulation::recurFunction
müssen sich nach der Größe und Anzahl der Scheiben immain
richten. D.h. wenn Du bei dem einen wesentliche Änderungen durchführt, dann muss das andere auch angepasst werden.
Außerdem ist Simulation::recurFunction direkt von Display abhängig, da es dort als lokale Variable benutzt wird. Das ist zumindest aus Sicht des Designs problematisch - natürlich weniger bei so einem kleinen Programm. Bei größeren Applikationen sollte man das besser trennen.Ich könnte mir einen move-Algorithmus vorstellen, der etwa so aussieht:
template< typename View > void move( std::size_t n, Tower& src, Tower& via, Tower& dst, const View& view ) { if( n == 1 ) { move1( src, dst ); view(); } else { move( n-1, src, dst, via, view ); move1( src, dst ); view(); move( n-1, via, src, dst, view ); } }
move1
ist eine (friend-)Funktion zum Verschieben eine Scheibe vom Turmsrc
(Source) nachdst
(Destination).view
ist ein Funktor, in dem der Anwender die Ausgabe der Szene unterbringen kann.Cpp_Anfaeger schrieb:
Werner Salomon schrieb:
Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen
Ja, ich habe den Hang, alles so zu programmieren, damit ich sofort sehe, was vor sich geht.
Das solltest Du auch so beibehalten. Was mich störte war eigentlich nur das S
lice::setSlice
. Folgendes wäre auch noch eine Alternative:Slice slices[] = { Slice( "***" ), Slice( "*******" ), Slice( "***********" ), Slice( "***************" ), Slice( "*******************" ), Slice( "***********************" ) }; // 3 Türme erstellen + Turm_1 mit Scheiben auffüllen Tower tower_1( begin(slices), end(slices) ), tower_2, tower_3; // Tower tower_1( slices, slices + sizeof(slices)/sizeof(*slices) ), tower_2, tower_3; // bei C++98
Dann brauch es noch einen weiteren Konstruktor:
class Tower { public: Tower(); template< typename InItr > Tower( InItr first, InItr last ) : m_tower_content( first, last ) {}
.. wobei ich auch auch auf die Slice-Klasse ganz verzichten würde. Es reicht, im Turm eine Menge von (Scheiben-)Ids abzulegen - von 1 für die kleinste Scheibe und dann fortlaufend aufsteigend. Und erst bei der Anzeige wird entschieden, wie eine Scheibe genau aussieht.
Gruß
Werner
-
Werner Salomon schrieb:
In Der Methode Display::gotoXY() hast Du X und Y vertauscht
Hmmm ... eigentlich dachte ich, dass Bill das in seiner Klasse vertauscht hatte
, weil ich das Ganze in meiner Klasse umdrehen musste, damit aus x = line wird bzw. y = column:
void Display::gotoXY(const int &line, const int &column) { COORD p; p.Y = line; // Zeile p.X = column; // Spalte SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p); }
Werner Salomon schrieb:
Was mich störte war eigentlich nur das Slice::setSlice.
Mich hat es auch gestört. Super ... wieder was gelernt ... ich hatte vorher nicht herausfinden können, wie man die Initialisierung von 6 Elementen auf einmal syntaktisch hinbekommt.
Zu den Templates ... ich denke, dass ich noch viel mehr mit Template machen muss. Es wird ja oft gesagt, dass man das nur kurz machen sollte, um zu wissen, was das ist, da man es später kaum benutzt. Bisher dachte ich, dass man nur Template benutzt, wenn es darum geht, Schreibarbeit für Überladungen zu sparen. Sollte man generell Template verwenden? Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?
-
Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?
Nicht direkt. Templates sparen nur Schreib- und Wartungs-Aufwand. Sie machen an sich den Code nicht schneller.
Sollte man generell Template verwenden?
Nein, absolut nicht!
-
Arcoth schrieb:
Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?
Nicht direkt. Templates sparen nur Schreib- und Wartungs-Aufwand. Sie machen an sich den Code nicht schneller.
Sollte man generell Template verwenden?
Nein, absolut nicht!
Habe ich mir auch gedacht ... bis ich diese Diskussion gelesen hatte. Ich weiß allerdings nicht, ob ich die Übersetzung richtig verstanden habe:
http://stackoverflow.com/questions/8925177/c-templates-for-performance
-
Cpp_Anfaeger schrieb:
Habe ich mir auch gedacht ... bis ich diese Diskussion gelesen hatte. Ich weiß allerdings nicht, ob ich die Übersetzung richtig verstanden habe:
http://stackoverflow.com/questions/8925177/c-templates-for-performance
Das ist auch richtig. Templates sind in den Fällen viel schneller. Aber du musst gucken, gegenüber was sie schneller sind: Da geht es um den Weg, wie man in C generisch programmiert. Das funktioniert nämlich damit, dass man in C das Typsystem aushebelt, was typabhängige Optimierungen ziemlich unmöglich macht.
template <typename T> void foo(T &t); void bar(int &i); void foobar(void *v);
Mal angenommen, alle davon würden das gleiche tun. Dann wären foo und bar in allen Fällen gleich schnell, denn foo<int> wäre identisch zu bar. Der Vorteil von foo wäre, dass man sich nicht auf int beschränkt hätte. foobar wäre aber möglicherweise langsamer als die beiden anderen Varianten, selbst wenn man es mit einem entsprechend gecasteten int* aufruft. Denn wenn der Code für foobar erzeugt wird, dann kann der Compiler nicht wissen, womit die Funktion später mal aufgerufen wird und kann diesbezüglich weniger optimieren.
-
Achso, danke alles klar ... nur gegenüber C in diesen Fällen.
-
Das hat mit dem Typsystem oder dessen Umgehung nicht unbedingt was zu tun, sondern damit, dass die Generik in dem Fall erst zur Laufzeit aufgelöst wird. Etwas entsprechendes kannst du in C++ mit virtuellen Funktionen bauen, das hat die gleichen Nachteile.