Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Di Mai 21, 2024 14:20

Foren-Übersicht » Programmierung » Einsteiger-Fragen
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 20 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
 Betreff des Beitrags: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 03:01 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
Hi,
dieses Stück Code hatte ich vor 4 Wochen geschrieben. Setzt man "itSnowsState=true", soll es anfangen zu schneien.
Allerdings schmiert die Routine zum zeichnen(glaube dort liegt der Fehler) ab, wenn man den Schnee direkt nach dem Programmstart einschaltet. Wenn es das Programm einige Frames schafft, läuft es fehlerfrei durch.
Ich denke der Fehler liegt in dem "drawMe" versteckt.

Da ich noch Listen schreiben muss mit nodes usw um meine Objekte später zu verwalten, wollte ich einfach etwas implementieren um zu sehen ob das so funktioniert wie ich mir das vorstelle.
Ich wollte eine linked List schreiben und dachte ein- und ausschaltbarer Schnee ist ganz gut um mich mit Pointern usw vertraut zu machen. Allerdings werde ich noch nicht richtig schlau draus, welche Syntax Pointer usw in C++ haben und wann ich Speicher wie wieder freigeben muss.
Denke das Programm schmiert wegen der schlampigen Verwaltung mit Nullpointern ab, allerdings ist C++ für mich fast völliges Neuland.
Freue mich schon auf Antworten.
Gruß, Skel

Header:
Code:
  1.  
  2. #ifndef SNOWFLAKE_H
  3. #define SNOWFLAKE_H
  4. #include <GL/glut.h>
  5. #include <cmath>
  6. #include <cstdlib>
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <iostream>
  10.  
  11.  class SnowFlake
  12. {
  13. public:
  14.     GLfloat x,y,z;
  15.     //GLfloat r,g,b;
  16.     bool flakeLanded;
  17.     int flakeLifeSpan;
  18.     SnowFlake *prior,*next;
  19.     static SnowFlake *Anker,*Last;
  20.     static int Flocken;
  21.     static bool itBlowsState;
  22.     static bool itSnowsState;
  23.  
  24.     void drawMe();
  25.     static void letItBlow();
  26.     static void letItSnow();
  27.     SnowFlake();
  28.     SnowFlake(GLfloat,GLfloat,GLfloat);
  29.     ~SnowFlake();
  30. };
  31. #endif
  32.  


CPP:
Code:
  1.  
  2. #include "SnowFlake.h"
  3.  
  4. SnowFlake *SnowFlake::Anker=NULL;
  5. SnowFlake *SnowFlake::Last=NULL;
  6. int SnowFlake::Flocken=0;
  7.  
  8. GLfloat windx=0, windz=0;
  9. GLfloat sx=0,sz=0;
  10. bool SnowFlake::itSnowsState=false;
  11.  
  12. void SnowFlake::letItSnow()
  13. {
  14.         {
  15.             if (SnowFlake::itSnowsState)
  16.             {
  17.             Flocken++;
  18.             SnowFlake *s=new SnowFlake(
  19.                 -50+100*static_cast <float> (rand()) / static_cast <float> (RAND_MAX),
  20.                 10,
  21.                 -50+100*static_cast <float> (rand()) / static_cast <float> (RAND_MAX));
  22.             if (Anker==NULL)
  23.                 {
  24.                     Anker=s; Last=s;
  25.                 }
  26.             else
  27.                 {
  28.                     Last->next=s;
  29.                     Last=s;
  30.                 }
  31.             }
  32.             if (Anker!=NULL)
  33.             Anker->drawMe();
  34.         }
  35. }
  36.  
  37.  
  38. void SnowFlake::drawMe()
  39. {
  40.     if (!this->flakeLanded)
  41.     {
  42.         this->x=this->x+sx;
  43.         this->z=this->z+sz;
  44.         this->y=this->y-0.1;
  45.     }
  46.  
  47.     glPushMatrix();
  48.     glTranslatef(this->x,this->y,this->z);
  49.     glColor3f(0.8,0.9,1);
  50.     glutSolidSphere(0.1,3,3);
  51.     glPopMatrix();
  52.  
  53.     if ((this->y<-0)&&(!this->flakeLanded))
  54.         {this->flakeLanded=true;this->flakeLifeSpan=200;this->y=0;}
  55.     if (this->flakeLanded)
  56.         this->flakeLifeSpan--;
  57.     if (this->flakeLifeSpan==1)
  58.     {
  59.         Flocken--;
  60.         if ((Anker==this)&&(Last==this))
  61.         {
  62.             Anker=NULL;Last=NULL;
  63.         }
  64.         else if (Anker==this)
  65.         {
  66.             Anker=this->next; this->next->prior=NULL;
  67.         }
  68.         else if (Last==this)
  69.         {
  70.             this->prior->next=NULL; Last=this->prior;
  71.         }
  72.         else
  73.         {
  74.             this->prior->next=this->next; this->next->prior=this->prior;
  75.         }
  76.     }
  77.     if (next!=NULL)
  78.         next->drawMe();
  79. }
  80.  
  81.  
  82. SnowFlake::SnowFlake(GLfloat x, GLfloat y, GLfloat z)
  83. {
  84.     this->x=x;
  85.     this->y=y;
  86.     this->z=z;
  87.     this->flakeLanded=false;
  88.     this->flakeLifeSpan=0; // 0 means unlimited lifespan
  89. }
  90. SnowFlake::SnowFlake()
  91. { }
  92. SnowFlake::~SnowFlake()
  93. { }
  94.  


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 03:21 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
Habe es eben herausgefunden:
Habe
Code:
  1.  
  2. if (this->next!=NULL)
  3.         next->drawMe();
  4.  

durch
Code:
  1.  
  2. if (this!=Last)
  3.         next->drawMe();
  4.  

ersetzt.

Jetzt geht es fehlerfrei. Trotzdem würde ich gerne wissen wieso das erstere nicht funktioniert hat...

Muss ich eigentlich den Destructor manuel aufrufen oder reicht es in dem Fall wenn kein Pointer mehr darauf zeigt damit es gelöscht wird?
Also wäre
Code:
  1.  
  2. if (this!=Last)
  3.         next->drawMe();
  4.         this->~SnowFlake();
  5.  

empfehlenswert?


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 10:05 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Skeltek hat geschrieben:
Muss ich eigentlich den Destructor manuel aufrufen oder reicht es in dem Fall wenn kein Pointer mehr darauf zeigt damit es gelöscht wird?
Alles, was du mit new erzeugt hast, musst du auch mit delete wieder freigeben und zwar genau einmal. Wenn du etwas zum zweiten Mal freigibst, stürzt dein Programm ab (meistens; Verhalten ist undefiniert). Wenn du etwas nicht freigibst, aber den Pointer auf einen anderen Wert setzt, hast du ein Memory-Leak. delete mit einem Null-Pointer aufzurufen ist hingegen erlaubt. Es ist daher nicht verkehrt, nach jedem delete den Pointer auf 0 zu setzen.

new allokiert den Speicher für das Objekt und ruft danach den Konstruktor auf. Analog dazu ruft delete zuerst den Destruktor auf und gibt danach den Speicher frei. Wenn du mit new nicht nur ein einzelnes Objekt, sondern gleich ein ganzes Array erzeugst, musst du statt delete übrigens delete[] nehmen.

Vorsicht bei sowas:
Code:
  1.  
  2. if (this!=Last)
  3.         next->drawMe();
  4.         this->~SnowFlake();
  5.  
Wie du den Code einrückst, ist dem Compiler vollkommen egal. Die Zeile 4 (die übrigens falsch ist) wird immer ausgeführt, egal ob die if-Bedingung wahr ist oder nicht. Wenn du mehr als eine Anweisung von einem if abhängig machen willst, musst du alle in einen Block mit geschweiften Klammern {} stecken.

Btw, du musst nicht jedesmal this-> schreiben, wenn du auf eine Membervariable oder -funktion zugreifen willst. Das ist nur notwendig, wenn es im aktuellen Scope eine Variable/Funktion mit dem selben Namen gibt.

Übrigens halte ich es für eine gute Übung, eine verkettete Liste ganz manuell mit new und delete zu implementieren. Ein Verständnis dafür, wie Pointer funktionieren, ist essentiell. Allerdings sei auch gesagt, dass man in realen/größeren Projekten natürlich std::list (und andere STL-Container) benutzt und sehr selten irgendwas mit new oder delete schreibt.

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 18:13 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Paar Anmerkungen: (Wichtigkeit absteigend)
  1. Den Destruktor solltest du nie selber aufrufen. (Es gibt Ausnahmen, aber die sind eigentlich kaum einer Erwähnung wert)
  2. Du solltest deinen Code konsistent umbrechen/einrücken, das hilft indirekt viele Fehler zu finden. (Dein Beispielcode mit dem Destruktoraufruf zum Beispiel ist von der Einrückung extrem irreführend. Aber auch sonst recht willkürlich.)
  3. "new" und besonders "delete" sind zurecht in modernen C++ inzwischen sehr verpönt. Am Besten nutzt man sie außerhalb von kleinen Übungen zu Pointern überhaupt nie. Leg deine Strukturen entweder direkt auf dem Stack bzw. deiner Datenstruktur ab, oder nutze Smart Pointer, meistens am Besten "std::unique_ptr". Das macht dann "delete" automatisch. Wenn man Exceptions verwendet ohne Smart Pointern in allen Codepfaden keine Speicherlecks zu haben, ist sonst nahezu unmöglich
  4. Inzwischen gibt es "nullptr" mit ein paar kleinen Vorteilen, "NULL" sollte man jedenfalls in neuen C++ Code nicht mehr einsetzen.
  5. Leere Destruktoren solltest du weglassen. Reduziert Codegröße und der Windows-Compiler generiert dafür scheinbar teilweise drastisch schlechteren Code.
  6. "this->x=this->x+sx;" kannst du so "x+=sx;" schreiben.
  7. Auf die Dauer solltest du dir eine Mathe-Lib mit einem Vektordatentyp holen, damit du nicht immer von Hand X, Y, Z deklarieren bzw. verrechnen musst. Ich habe mir meine mal selbst geschrieben. Ich habe hier auch schon öfter fertige Libs im Forum dafür gesehen, oder du nimmst einfach zum Beispiel GLM. Ich habe mir meinen Code dafür selbst geschrieben. Das wäre eine gute Übung für Templates, aber natürlich bisschen zeitaufwändig.

Nebenbei, eine verkettete Liste hat bedingt der Arbeitsweise von modernen CPUs ein sehr schlechtes Performanceverhalten, so das ein "std::vector" "eigentlich immer" eine bessere Alternative ist. (Link, Link, Link) Ich stimme außerdem unabhängig davon glAwesome zu, dass man so etwas normalerweise nur zu Übungszwecken machen sollte. Schreibt man das selbst, hat man potentiell eine Menge Bugs, Arbeit, schlechte Kapselung und schwer verständlichen Code. STL Container und STL Smart Pointer machen einem das Leben viel einfacher.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 19:14 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
Danke für die Antworten.
@glAwesome:
Komisch, dass es jetzt aber mit selbst aufgerufenem Destruktor trotzdem läuft und trotz des oben eingebauten Fehlers keine Flocken verfrüht löscht.
Der Code ist einen Monat alt, C++ war mir praktisch völlig fremd. In Java oder anderen Sprachen würde ich einfach 200 Zeilen Code tippen und es würde fehlerfrei laufen ohne Nachkorrektur.
Leider ist das nur eines meiner Vorlesungsfächer(studiere eigentlich Mathematik), entsprechend wenig Zeit habe ich mir die Sprache C++, openGL und Diverses gleichzeitig in Eigenregie anzueignen.
Welche Libs mir zur Verfügung stehen weiss ich so jetzt nicht, wir dürfen halt keine Libs zusätzlich zu denen bereits auf den Rechnern vorhandenen installieren.
Dachte selbstgeschriebene Liste könnte schneller laufen, da ich diverse Objektmethoden wie zählen der Gesammtzahl, Indizierung usw ja nicht benötige. Array ist afaik auch langsamer. Mit bereits implementierten Listen kenne ich mich nicht aus. Weiss nicht ob ich zeittechnisch hinbekomme da alles zu recherchieren.

@OpenglerF:
1. Vom Delete Befehl habe ich erst später erfahren, allerdings war unklar ausgedrückt wann man ihn verwenden muss und wann nicht. Leider auch Zeitmangel für Rcherche.
2. Einrücken mache ich normalerweise auch, aber das war so ein Hickhack mit dem rumeditieren, bis ich heraus hatte wie die C++ Syntax überhaupt zu halten ist :-)
3.
Zitat:
Leg deine Strukturen entweder direkt auf dem Stack bzw. deiner Datenstruktur ab,...
Habe mit der Teminologie noch so meine Schwierigkeiten. Meine Linked List ist doch ein Stack? Wegen den Speicherlecks und dem Speicherfreigeben habe ich ja extra den thread aufgemacht um zu fragen wie man das genau und wann macht :-)
4. Nullptr habe ich bereits gesucht, allerdings bisher nicht herausgefunden, in welcher Lib er zu finden ist. Soweit ich gelesen habe benutzt man ihn, um Pointer x mit Nullpointer zu vergleichen. Wenn ich aber einen "Pointer" auf "null" setzen möchte, soll ich das trotzdem mit "=NULL" machen hat man mir mal gesagt :-(
5. Wenn der Windows Compiler schlechteren Code generiert, wieso dann automatisch erstellen lassen? Finde ich jetzt nur etwas missverständlich ausgedrückt bzw verstehe ich nicht 100%. Verstehe auch nicht ganz, was das mit einem Windows Compiler zu tun hat :-/
6. Das ist so, damit mein Teamkollege das auch rafft :-)
7. Hilfsklasse mit Matheformeln habe ich selbst geschrieben. Dachte es dauert länger alle Befehle zu recherchieren als sie kurz selbst zu schreiben. Vom Schlagwort Templates habe ich bisher nur gehört.

Ich sehe mir mal deine Links an und schaue dann mal weiter.
Danke nochmal.
Gruß, Skel

ps: Habe erstmal das erste Video gesehen. Die Wahl fiel gerade auf selbstgeschriebene Linked List, da ich bei der späteren Implementierung sequentiell jedes Objekt verwende und abarbeite und keine gezielt suche. Die Schneeflocken sind auch sequentiel generiert, werden in der gleichen Reihenfolge gelöscht um die Fragmentierung des Arbeitsspeichers klein zu halten.
Plan war LinkedList zur Generierung aller statischen Objekte zu verwenden und sobald die Größe der fertigen LinkedList feststeht, den Inhalt in einen Array zu übertragen.
Leider bin ich kein Informatiker, das meiste Wissen kommt aus der Sparte "wie würde ich es machen" um hinterher festzustellen, dass es meist so gemacht wird :-)
Auch haben meine Objekte alle Unterschiedliche Größen, weshalb darin dann ohnehin wieder Pointer nötig würden, da ich keinen Stack anlegen kann, wo die Objektgrößen noch nicht feststehen.
Aber ich gucke mir jetzt mal die anderen Videos an und schau, was davon ich bei dem Zeitdruck alles mitnehmen kann zum im eigenen Projekt verwenden.

pps: Habe mir jetzt alle Videos angesehen und die Links durchgelesen.
Da meine Daten egal bei welcher Sortierung wohlgeordnet sind, keine Datensätze gesucht werden müssen, nich neu gehashed werden muss, die Datengröße zu groß ist und variabel, da sie sequentiell abgearbeitet werden sind für mich Lists die beste Wahl.
Zitat:
•Non-trivial data type: use std::list unless you need the container especially for searching. But for multiple modifications of the container, it will be very slow.
•Push to front: use std::deque or std::list

Das beschreibt glaube auch am besten meine Einsatzzwecke. Ich hatte auch besonderen Wert darauf gelegt keine einzelnen Elemente gezielt von aussen aufzuspüren/zu suchen, auszulesen und dann als Parameter einsetzen zu müssen, sondern den gesammten Datensatz einfach sequentiell abarbeiten zu können.
Jeder Datenstruktur sein Einsatzzweck ;-)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 22:10 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Skeltek hat geschrieben:
3.
Zitat:
Leg deine Strukturen entweder direkt auf dem Stack bzw. deiner Datenstruktur ab,...
Habe mit der Teminologie noch so meine Schwierigkeiten. Meine Linked List ist doch ein Stack? Wegen den Speicherlecks und dem Speicherfreigeben habe ich ja extra den thread aufgemacht um zu fragen wie man das genau und wann macht :-)
Mit Stack sind hier die Funktions-lokalen Variablen ohne Pointer gemeint.
Code:
  1. void foo() {
  2.    Bar bar1;
  3.    Bar *bar2 = new Bar();
  4.    delete bar2;
  5. }
bar1 liegt auf dem Stack, das Objekt zu bar2 auf dem Heap. Der Speicher zu bar1 wird automatisch allokiert und freigegeben, den für bar2 musst du dir selber holen (und wieder zurückgeben), mit new und delete. In aktuellem (C++11) C++ macht man das dann so:
Code:
  1. void foo() {
  2.    Bar bar1;
  3.    std::unique_ptr<Bar> bar2(new Bar());
  4. };
Hier kümmert sich die Standard Library darum, dass der Speicher für bar2 freigegeben wird. bar2 kann man verwenden wie einen normalen Pointer, mit Einschränkungen⁽¹⁾.

Skeltek hat geschrieben:
4. Nullptr habe ich bereits gesucht, allerdings bisher nicht herausgefunden, in welcher Lib er zu finden ist. […]
Das spricht dafür, dass du C++11 nicht verwendest – nullptr ist ab C++11 ein keyword, also global und überall definiert, ohne includes. C++11 würde ich dir dringend ans Herz legen. Beim gcc (und clang) bekommt man C++11 mit dem Compilerflag -std=c++11.

Skeltek hat geschrieben:
5. Wenn der Windows Compiler schlechteren Code generiert, wieso dann automatisch erstellen lassen? Finde ich jetzt nur etwas missverständlich ausgedrückt bzw verstehe ich nicht 100%. Verstehe auch nicht ganz, was das mit einem Windows Compiler zu tun hat :-/
Die Aussage war, dass der Windows-Compiler (gemeint ist der Compiler von Microsoft der mit Visual Studio kommt, der MSVC) schlechteren Code generiert, wenn ein (leerer) Destruktor vorhanden ist. Was das mit dem zu tun hat, ist ganz einfach: Unterschiedliche Compiler erzeugen unterschiedlichen Code (ob speziell der MSVC für dich relevant ist, weiß ich nicht).

viele Grüße,
Horazont

(1): std::unique_ptr sagt etwas über die Eigentümerschaft zum Pointer aus. Wer den std::unique_ptr zu einem Pointer besitzt, besitzt das Objekt. Das wird erzwungen, indem std::unique_ptr-Objekte nicht kopierbar sind, sondern nur verschiebbar (mit std::move, z.B.:
Code:
  1. void foo(std::unique_ptr<Bar> &&obj) {
  2.    // takes ownership of obj, *stores* it somewhere!
  3. }
  4.  
  5. void baz() {
  6.    // make an object
  7.    std::unique_ptr<Bar> bar(new Bar());
  8.    // and transfer ownership
  9.    foo(std::move(bar));
  10. }
)

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 22:12 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Komisch, dass es jetzt aber mit selbst aufgerufenem Destruktor trotzdem läuft und trotz des oben eingebauten Fehlers keine Flocken verfrüht löscht.

Schau dir mal die Klasse "SnowFlake" an, dessen Destruktor aufgerufen wird. Was machst du dort? Nichts. Und die Member sind alle eingebaute Typen die selbst auch keine Destruktion erfordern. Im Prinzip macht der Destruktor einfach nichts. Ich bin mir aber relativ sicher, dass das laut C++ Standard trotzdem illegal wäre. (Und das es sonst meist nicht funktioniert, sollte auch klar sein)

Zitat:
Welche Libs mir zur Verfügung stehen weiss ich so jetzt nicht, wir dürfen halt keine Libs zusätzlich zu denen bereits auf den Rechnern vorhandenen installieren.

Also jeder C++ Compiler liefert dir die C-Stdlib und die STL(Standard Template Library). Die C-Standard Bibliothek liefert einige Dinge wie zum Beispiel die Mathefunktionen, die man im Prinzip auch in C++ noch nutzt. Diese Header gibt es in C++ in zwei Varianten: mit ".h" am Ende oder mit "c" am Anfang. Also "cmath" und "math.h", "cstdlib", "stdlib.h". War mir vorhin nicht aufgefallen, aber du bindest beide ein, was sinnlos ist, die C++ Variante "cstdlib" reicht.
Und dann gibt es eben noch die STL. Wie der Name schon angibt wird dort viel mit Templates gearbeitet... Aber, ist meistens sehr einfach zu verwenden. Also keine Sorge. ;-) Dort gibt es eben auch so Container wie den "std::vector" oder eben die "std::list", auch wenn man von der in der Regel besser die Finger lässt. Oder es gibt Smart-Pointer, die automatisch "delete"n. Die lassen sich dann fast so verwenden wie Container und Referenzen in Java.

Und die meisten Libs muss man nicht "installieren". Zumindest kleinere Dinge wie zum Beispiel GLEW oder GLFW, bestehen aus ein paar Sources und Headern die du einfach in dein Projekt einbinden kannst. Wer es gerne etwas komplizierter haben will, kann sich auch fertig kompilierten Versionen suchen und dazulinken.

Zitat:
Dachte selbstgeschriebene Liste könnte schneller laufen, da ich diverse Objektmethoden wie zählen der Gesammtzahl, Indizierung usw ja nicht benötige

Ich weiß nicht was du mit Indizierung meinst. Habe ich noch nie gehört. Auf jeden Fall sind die Container in C++ prinzipiell als sogenannte "Zero Overhead Abstraction"s ausgelegt. Klar, manchmal ist das vielleicht minimal zu viel versprochen, aber prinzipiell können sie diesem Grundsatz weitgehend gerecht werden. Ich halte es für nicht zu unwahrscheinlich das dein Eigenbau langsamer läuft. Einfach weil die fertigen Libs schon seit Jahren optimiert werden konnten. Außerdem sind sie so gut wie garantiert Bugfrei. Und einfacher. Und dann bieten sie viel mehr Hilfsfunktionen und bieten Debug-Hilfen. Die Container von Visual Studio können im Debug Modus zum Beispiel sehr viele Möglichkeiten der falschen Verwendung aufspühren.

Zitat:
Array ist afaik auch langsamer.

:shock: Ein Array ist praktisch das Effizienteste überhaupt. Die Dinger werden einfach ohne alles hintereinander in den Speicher geschrieben. 0 Speicheroverhead. 0 Rechenoverhead. Keine Pointer. Wenn du die Anzahl vorher weißt, mach ein Array, wenn nicht mach ein "std::vector". Der ist im Prinzip ein dynamisch wachsendes Array.

Zitat:
Habe mit der Teminologie noch so meine Schwierigkeiten. Meine Linked List ist doch ein Stack?

Man merkt, dass du von Java kommst. ;-) Genau genommen lässt sich das mit dem Code von dir nicht genau sagen. Linked List macht im Prinzip normalerweise auf dem Stack keinen Sinn. Stack heißt im Prinzip "als lokale Variable". (Die sind sehr schnell zu erstellen und zu zerstören)
Also statt...
Code:
  1.  
  2. void DoSomething()
  3. {
  4.     int* Blub = new int(); //Auf dem Heap; Kann jeder Typ sein. Und nur der Einfachheit halber im Beispiel verzichte ich auf Smart Pointer. ;-)
  5. }
  6. //... dieses...
  7. void DoSomething()
  8. {
  9.     int Blub; //Auf dem Stack
  10. }
  11.  


Manchmal ist diese Aussage auch auf Datenstrukturen gemeint. Also statt...
Code:
  1.  
  2. class MyClass
  3. {
  4.     MyOtherClass This; //Direkt in die Datenstruktur eingebettet.
  5. };
  6. ///... dieses...
  7. class MyClass
  8. {
  9.     MyOtherClass* This; //Mit zusätzlichen Verweis auf eine andere Stelle. Außerdem Gefahr von Speicherleck.
  10. };
  11.  

Vorteil sind weniger Allokationen auf dem Heap(Also ein Datenblock den du "irgendwo im Speicher" mit "new" anlegst) und gar keine Möglichkeit mehr von Speicherlecks. Das ganz ohne smarte Pointer. Das wird nämlich von ganz alleine freigegeben, wenn der Scope(beim Stack im Beispiel oben) verlassen wird, bzw. das übergeordnete Objekt("MyClass") zerstört wird.

Zitat:
Nullptr habe ich bereits gesucht, allerdings bisher nicht herausgefunden, in welcher Lib er zu finden ist.

Ja, er ist in keiner Lib. Und das ist eine seine große Stärke, du kannst ihn einfach verwenden. Als Schlüsselwort. Ohne Lib. Ohne Fallstricke.
Bei einigen Compilern musst du ggf. C++11 aktivieren(-std=c++11 oder -std=c++0x), beim Microsoft Visual C++ Compiler, falls du den verwendest, ist das nicht mal notwendig.

Zitat:
Wenn der Windows Compiler schlechteren Code generiert, wieso dann automatisch erstellen lassen? Finde ich jetzt nur etwas missverständlich ausgedrückt bzw verstehe ich nicht 100%. Verstehe auch nicht ganz, was das mit einem Windows Compiler zu tun hat :-/

Also semantisch macht es eigentlich absolut keinen Unterschied. Im Prinzip ist es einfach ein Bug. Andere Compiler haben dieses Problem nicht. Um das Problem zu umgehen, einfach weglassen wenn er eh leer ist.

Zitat:
Hilfsklasse mit Matheformeln habe ich selbst geschrieben.

Warum nutzt du sie dann hier nicht? Dafür sollte es eine Vektor-Klasse geben. An der Stelle allein kein großer Gewinn, aber im ganzen Programm immer X,Y und Z von Hand zu handhaben ist ziemlicher Codeoverhead.

Zitat:
Vom Schlagwort Templates habe ich bisher nur gehört.

Templates verwenden, solltest du dir schnell angewöhnen(STL ;-)), dann selbst Templates schreiben kannst du wahrscheinlich noch etwas herauszögern. Ein Template ist im Prinzip einfach eine parametrische Klasse(oder Funktion oder Konstante). Im sehr weiten Sinn, sind es ein (sehr viel mächtigeres)Äquivalent zu Generics aus C#/Java. Im Falle das Vektors wäre es zum Beispiel redundanter Code, wenn man einen Vektor für Floats, einen für Doubles und einen für Ints(und alle anderen möglichen Typen) schreiben würde, je nach dem was man gerade braucht. Oder man kann eine Funktion schreiben, die die Länge eines beliebigen Vektors berechnet. Egal ob Float, Int, Vektor im 2 dimensionalen Raum oder Vektor im 3 dimensionalen Raum oder ein x dimensionaler Vektor.

Zitat:
Die Wahl fiel gerade auf selbstgeschriebene Linked List, da ich bei der späteren Implementierung sequentiell jedes Objekt verwende und abarbeite und keine gezielt suche.

Also zum einen ist das Erstellen langsamer bei einer Linked List. Es müssen x freie Plätze im RAM gesucht werden; beim Vektor wird bloß ein großer gebraucht. (Weniger Fragmentierung geht nicht) Der nächste noch entscheidendere Punkt, den du "abarbeiten" nennst, ist aber fast noch entscheidender. Das ist vergleichsweise RICHTIG langsam, weil jedes mal das Ziel wieder ein unbekannter Standort hinter einem Pointer ist. Ich denke, dass wird in dem Video auch gut erklärt.
Die theoretische Informatik würde dir wahrscheinlich sagen, dass die List und der Vektor in dem Fall das gleiche Laufzeitverhalten hat. Leider sieht es in der Praxis ein wenig anders aus. Ich glaube, das wird in dem Video auch erwähnt: Verwende einfach einen Vektor. :wink:

Zitat:
Das beschreibt glaube auch am besten meine Einsatzzwecke.

"std::list unless you need the container especially for searching." ist ein bisschen missverstänlich ausgedrückt. Er bezieht sich damit auf seinen Test mit der linearen Suche. Lineare Suche ist es, einfach alle Elemente nach ein andere durchzuiterieren, bis ein bestimmtes gefunden wurde. Das entspricht exakt deinem Zugriffsmuster wenn du deine Schneeflocken wahrscheinlich in jedem Frame mindestens einmal durchiterierst um sie zu rendern. Und das ist "relativ" langsam. Weil es eine endlose Pointerkette ist. Pointer zu Struktur mit Pointer zu Struktur mit Pointer zu Struktur mit Pointer zu Struktur mit Pointer... Wenn du auch noch bestimmte Schnellflocken zur Laufzeit erstellst oder löscht, entspricht das dem "Random Remove" und "Random Insert" Benchmark. Kannst du dir ja selbst ansehen.

Zitat:
Jeder Datenstruktur sein Einsatzzweck

Joa, stimmt, allerdings mir bisher der für eine "std::list" in C++ noch nicht untergekommen. ;-)
Das "Push to front" Argument macht schon Sinn, aber wie häufig kommt es vor das man einen großen Container hat und unbedingt am Anfang etwas ändern muss? Sehr sehr selten, meistens kann man einen Algorithmus so umstellen, dass man am Ende arbeiten kann.

Abschließend möchte ich nochmal erwähnen, dass deine Erwähnen, dass die Performance durch all diese Dinge nur gering im Vergleich zu dem was du sonst machst belastet, wird. Sowas wie "glutSolidSphere" mit Draw Call für jede Flocke überschattet jede Ineffizienz in C++. Wenn es dich also irgendwie glücklich macht, könntest du theoretisch auch eine "std::list" verwenden. Mir ging es eigentlich hauptsächlich um die Codeordnung. :wink:

EDIT:
Mein Beitrag IST zu lang geworden. :shock:


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mo Dez 01, 2014 22:17 
Offline
DGL Member
Benutzeravatar

Registriert: Mi Aug 14, 2013 21:17
Beiträge: 588
Programmiersprache: C++
Skeltek hat geschrieben:
Komisch, dass es jetzt aber mit selbst aufgerufenem Destruktor trotzdem läuft und trotz des oben eingebauten Fehlers keine Flocken verfrüht löscht.
Weil du durch das Aufrufen des Destruktors nicht das Objekt löschst (im Sinne von Speicher freigeben).
Code:
  1. this->~SnowFlake();
  2. // versus
  3. delete this; // aber niemals wirklich so schreiben!
Die erste Zeile ruft einfach nur den Destruktor der Klasse SnowFlake auf, welcher im Prinzip nichts weiter als eine Funktion ist. In deinem Fall tut sie gar nichts, denn die geschweiften Klammern bei der Definition des Destruktors sind leer. Das ist also insofern "falsch" (Zitat ich selbst), als es nicht das tust, was du vorhast (das Objekt löschen). Syntaktisch ist es jedoch offenbar korrekt, weshalb sich der Compiler bei dir auch nicht beschwert.
Zeile 3 hingegen ruft den Destruktor auf UND gibt anschließend auch den Speicher frei - was man wie gesagt nur ein einziges Mal machen kann.

Skeltek hat geschrieben:
Welche Libs mir zur Verfügung stehen weiss ich so jetzt nicht, wir dürfen halt keine Libs zusätzlich zu denen bereits auf den Rechnern vorhandenen installieren.
Die STL (Standard Template Library) kannst du aber benutzen, sie steht auf jedem System mit C++-Compiler zur Verfügung, gehört praktisch zur Sprache dazu.

_________________
So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Di Dez 02, 2014 14:52 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
Lord Horazont hat geschrieben:
Skeltek hat geschrieben:
4. Nullptr habe ich bereits gesucht, allerdings bisher nicht herausgefunden, in welcher Lib er zu finden ist. […]

Das spricht dafür, dass du C++11 nicht verwendest – nullptr ist ab C++11 ein keyword, also global und überall definiert, ohne includes. C++11 würde ich dir dringend ans Herz legen. Beim gcc (und clang) bekommt man C++11 mit dem Compilerflag -std=c++11.

Danke, mir kommt es nur vor wie ein Mammutprojekt. Selbst Java haben wir im ersten Semester nur rudimentär abgehandelt.
Nullptr wird vom parser zwar eingefügt aber er sagte immer "not declared". Wusste nicht ob das an einem fehlenden "include" liegt oder ob ich ihn falsch verwende.
Wird mir in Zukunft jedenfalls sehr helfen.

Zum durchiterieren:
Ja, ich dachte würde ich eine ganz bestimmte Flocke suchen um sie darzustellen oder die flocken sortieren wollen, wäre jede andere Option ausser einer Linked List besser.
Er geht ja nicht um Flocke N darzustellen nochmal bei A los, sondern springt ja direkt von M zu N.
Frage war auch, ob sich das zwischen Daten auslesen und Funktionen aufrufen unterscheidet. Dachte wenn jede Flocke ihre eigene Zeichenmethode hat, und durchgereicht wird geht das schneller als die Flocken einzeln auszulesen und als Parameter an eine static Metode zu übergeben.
Aber ich werde mir das auf jeden Fall nochmal ansehen und damit beschäfftigen. Versteht mich nicht falsch, ich werde mir die Tips auf jeden Fall im Kopf behalten. Für einen langfristigen Nutzen werde ich mir aber irgendwann die Zeit nehmen müssen, mir die Systematik des ganzen nochmal klar zu machen.

Lieber Nachvollziehen & Verstehen, statt glauben & einfach übernehmen denke ich :-)

Zwergfrucht hat geschrieben:
Ich glaube, dass diese Antwort von OpenglerF und glAwesome sehr gut und sinnvoll ist.

Völlig offensichtlich ist sie das :-)
Wir haben (glaube ich mich zu erinnern) zwar grob im ersten Semester mal gelernt(da hatten wir eigentlich nur etwas Java und Theorie), dass man bei Konstruktoren mit Übergabeparametern usw initialisieren kann usw, aber über die Verwendung von Destruktoren kam da nich viel(Macht Java ja anscheinend automatisch).
Fast mein ganzes Wissen übers Programmieren habe ich mir selbst angeeignet, das Wissen der ersten beiden Semester von der Uni/Hochschule würde nicht annähernd reichen um so ein Projekt durchzuziehen.
Andererseits sind dann auswendig gelernte Banalitäten für die Note ausschlaggebend.
Ich weiss, wie schwierig es ist sich durch die ganzen Informationsquellen zu wühlen, bis man eine gute gefunden hat.
Nochmal braucht man Zeit um das Wissen dieser Quellen zu extrahieren.
Und nochmal Zeit um dieses Wissen zu kombinieren.
Dann braucht man noch Zeit um eben die paar wenigen Quellen zu finden, welche die Lücken die man noch hat ausfüllen können.
Daß man das Wissen hier so kondensiert serviert bekommt weiss ich wirklich sehr zu schätzen; es ist ja keine Selbstverständlichkeit.
Erinnert mich etwas an die Hochschule, bei der die anderen gar nicht mal ahnen, wie viel ihnen dort vorgekaut wird :-)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Di Dez 02, 2014 19:09 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
Noch ein Tipp: Wenn dir irgendjemand etwas über "modernes" C++ oder STL und co. erzählt, ignoriere diese Leute. Nahezu 100% von dem was die Leute dir erzählen wird falsch sein. Und dabei ist völlig egal ob sie es ablehnen oder hoch loben. Denn die meisten Aussagen kommen durch Halbwissen oder Fanboyism :)

Vergleich einfach mal die Aussagen bezüglich std::vector und std::list hier mit denen von Microsoft:
-The choice of container type should be based in general on the type of searching and inserting required by the application.
-Vectors should be the preferred container for managing a sequence when random access to any element is at a premium and insertions or deletions of elements are only required at the end of a sequence.
-The performance of the class deque container is superior when random access is needed and insertions and deletions at both the beginning and the end of a sequence are at a premium.
-The list Class container is superior with respect to insertions and deletions at any location within a sequence.

Und jetzt frag dich mal was das normale Array macht :)

Fakt ist: Das ganze Zeug sind spezielle Bibliotheken und Version, die nicht immer eingesetzt werden. Du musst also nur wissen das es sie gibt, und wenn du sie brauchst musst du wissen wo du suchen kannst (Beim Hersteller? :P). Du kannst schließlich nicht alles Auswendig kennen.

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Di Dez 02, 2014 20:04 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
yunharla hat geschrieben:
Noch ein Tipp: Wenn dir irgendjemand etwas über "modernes" C++ oder STL und co. erzählt, ignoriere diese Leute. Nahezu 100% von dem was die Leute dir erzählen wird falsch sein. Und dabei ist völlig egal ob sie es ablehnen oder hoch loben. Denn die meisten Aussagen kommen durch Halbwissen oder Fanboyism :)

Klar, deshalb ja
Zitat:
Lieber Nachvollziehen & Verstehen, statt glauben & einfach übernehmen denke ich

Ich bin es auch aus anderen Gebieten gewohnt, dass ein Wissensvorsprung keine Garantie auf Richtigkeit aller Aussagen macht.
Wir hatten letztes Semester Sortierverfahren gelernt bei Datenstrukturen. Die anderen haben Bubblesort geübt und ich hab gleich die ganze Pallette mit Hashes, Buckets usw kombiniert 8)
Trotzdem sollte man sich alles anhören und nachprüfen; Erfahrungsvorsprung anderer auf einem Gebiet gibt trotzdem oft Hinweise auf Ausnahmefälle, Besonderheiten die das Intuitive umkehren oder irgendeinen wahren versteckten Kern.

Wenn ich mir heute klarmache, was Christian damals für einen Scheiss erzählt hat vor 10 Jahren... aber er hatte halt Informatik studiert und konnte mit Fachbegriffen und Halbwahrheiten um sich schlagen wo das soziale Umfeld aus Erfahrungsmangel nicht gegenargumentieren oder berichtigen konnte.
Andererseits liegen sogar Laien oft richtig, können es aber nicht begründen oder in Worte fassen.
Alles anhören, nachdenken, eigene Meinung bilden :-)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Di Dez 02, 2014 21:13 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Noch ein Tipp: Wenn dir irgendjemand etwas über "modernes" C++ oder STL und co. erzählt, ignoriere diese Leute. Nahezu 100% von dem was die Leute dir erzählen wird falsch sein.

Kann ich persönlich so gesagt nicht bestätigen. Ich hoffe auch mal, du meinst damit nicht meine Aussagen.
Es geht einfach bloß darum mit weniger Tipparbeit robusteren Code zu schreiben und insgesamt weniger viel falsch machen zu können.

Zu Beachten ist, dass die Aussagen die du zitierst sich nicht unbedingt auf die praktische Performance bei der normalen Verwendung bezieht. Gerade Performance im dem Gebiet auch etwas, dass sich in gewandelt hat. Ich halte es für sehr plausibel, dass das vor 10 Jahren geschrieben wurde. Cache zum Beispiel war damals allgemein noch kein so großes Thema.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mi Dez 03, 2014 08:01 
Offline
DGL Member

Registriert: Do Nov 20, 2014 03:18
Beiträge: 36
Wohnort: Stuttgart
Programmiersprache: C++ (Java, Ada,...)
Sind mit zusammenhängend hier die physischen oder phänomenologisch effektiven Speicheradressen gemeint?
Ich kenne die gängige Terminologie leider nicht, nur die Funktionsweise sehr grob. Ist es nicht so, dass es von der Körnung bzw der Adressbreite abhängt?
Also dass der Speicher im Adressregister zusammenhängend ist aber nicht im Speicher selbst?
Wieviele Byte liest der Cache genau voraus? Soweit ich es in Errinerung habe, liest der Chache lediglich die nächsten x Byte der im Adressregister zusammenhängenden Klumpens aus?
Könnte mir vorstellen, dass der Cache ab einer gewissen Größe der definierten Strukturen ohnehin ständig Adressen hin und her springen muss, wobei der tatsächliche Speicherort ohnehin für die Zugriffszeit völlig irrelevant wird. Zusammenhängend bedeutete für mich bisher nur, dass die Einträge im Adressregister hintereinander stehen und nicht tatsächlich hintereinander im Speicher vorliegen...

Deshalb lege ich ja so viel Wert auf das Verstehen der genauen Funktionsweise.
In der Regel gebe ich meinen Kommilitonen die noch viel weniger Ahnung haben auch generelle Tipps, daß "A" in der Regel meist besser ist als "B", da sie ohnehin nicht kapieren würden, in welchen Ausnahmefällen doch "B" besser wäre.
Jedenfalls erspart mir das hier einen Haufen Recherche wenn ich sehe, wie die gängigen Methoden systematisch zusammenhängen.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mi Dez 03, 2014 08:54 
Offline
DGL Member
Benutzeravatar

Registriert: Di Dez 27, 2005 12:44
Beiträge: 393
Wohnort: Berlin
Programmiersprache: Java, C++, Groovy
glutSolidSphere ist etwas heavy. Ich würde eher Sprites empfehlen, man kann Schnee auch prima in 2D über den Framebuffer rieseln lassen :P

_________________
Wenn Gauß heute lebte, wäre er ein Hacker.
Peter Sarnak, Professor an der Princeton University


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Schnee!
BeitragVerfasst: Mi Dez 03, 2014 17:45 
Offline
DGL Member

Registriert: Do Dez 29, 2011 19:40
Beiträge: 421
Wohnort: Deutschland, Bayern
Programmiersprache: C++, C, D, C# VB.Net
Zitat:
Sind mit zusammenhängend hier die physischen oder phänomenologisch effektiven Speicheradressen gemeint?

Es sind natürlich die "phänomenologisch effektiven Speicheradressen" gemeint(heißt übrigens virtueller Adressraum), eine normale Anwendung hat schließlich keine Möglichkeiten(bzw. Rechte) die physikalische Verteilung irgendwie festzulegen.

Das spielt jedoch zu der Sache eigentlich keine oder kaum eine Rolle weil die Adressumrechnung automatisch von der CPU erledigt wird. Als Anwender bekommt man normalerweise davon nichts mit. Außerdem ist eine Page(ein Block virtueller Speicher) zum Beispiel auf der x86/x64 Architektur mindestens 4KB groß, was schon wieder relativ viele Daten umfasst.

Zitat:
Wieviele Byte liest der Cache genau voraus?

Ein Cache ist aus Cache-Lines organisiert. Auf der x86/x64 Architektur immer 64 Bytes groß. Wenn du irgendeinen Wert liest, wird immer die ganze Cache-Line vom RAM angefordert(langsam) und steht dann komplett zur Verfügung. Deshalb macht es Sinn zum Beispiel keine Pointer zu anderen Listeneinträgen dazwischen zu haben die Platz im Cache wegnehmen. Außerdem sind die Datenblöcke wild über den Speicher verteilt, dass heißt in die Cachelines fallen immer wieder Daten außenrum und die sind von einer ganz anderen Stelle.

Zitat:
Könnte mir vorstellen, dass der Cache ab einer gewissen Größe der definierten Strukturen ohnehin ständig Adressen hin und her springen muss, wobei der tatsächliche Speicherort ohnehin für die Zugriffszeit völlig irrelevant wird. Zusammenhängend bedeutete für mich bisher nur, dass die Einträge im Adressregister hintereinander stehen und nicht tatsächlich hintereinander im Speicher vorliegen...

Da gibt es außerdem noch einen anderen Effekt, der eher indirekt mit dem Cache zusammenhängt.
Die Prefetch-Mechanismen des Prozessors. Normalerweise sollte ein Prozessor automatisch versuchen den Speicher der vermutlich bald gebraucht wird, vorauszuladen. Wenn du also ein (wenn auch nur im virtuellen Adressraum) zusammenhängendes Array hast, wird immer im selben Muster zugegriffen. Das vereinfacht natürlich die Vorhersage. Das Mapping im virtuellen Adressraum ändert sich auch nicht "ständig"(aus Sicht der Arbeitsgeschwindigkeit der CPU). Damit sollte die CPU vorausplanen können. (Und selbst wenn es häufig falsch vorausgesagt wäre... In 4KB passen häufig eine Menge Container-Elemente. )

Zitat:
Deshalb lege ich ja so viel Wert auf das Verstehen der genauen Funktionsweise.

Eine gute Einstellung. :D

Wenn du die Performance in deinem Anwendungsfall verbessern willst, möchte ich mal auf dj3hut1s Tipp hinweisen. :wink:
Mit Shadern kann man mit "Sprites" sogar mindesten so schöne Sachen darstellen wie mit tatsächlichen Objekten. :P


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 20 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Foren-Übersicht » Programmierung » Einsteiger-Fragen


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 5 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.011s | 14 Queries | GZIP : On ]