Hilfe! Member-Konstanten gleich als Feldindex?



  • Hallo,

    warum nimmst du keinen vector?



  • Feldgrößen die du nicht dynamisch (mit new) anlegst müssen zur kompilezeit bekannt sein. (wie es in dutzenden anderen beiträgen schon beschrieben wurde)

    _AREA_SIZE wird im Konstruktor initialisiert und ist somit nicht zur kompilezeit bekannt.

    Gruß Mirauder Mo



  • Ich denke wenn die Dimensionen des _height_field Arrays dynamisch sein müssen kannst du die Größe nur im Konstruktor festlegen. also zb statt

    float        _height_field[_AREA_SIZE][_AREA_SIZE];
    

    machst du

    float **_height_field;
    

    und reservierst den Speicher für das Array im Konstruktor
    also:

    _height_field = new float*[_AREA_SIZE];
    for(int i = _AREA_SIZE-1; i >= 0; --i)
      __height_field[i] = new float[_AREA_SIZE];
    


  • @Mis2com: Ok, geändert.

    @CarstenJ: Würd ich ja gerne, aber geht das in 2d? Und so, dass ich nicht oder nur unwesentlich mehr Speicher benötige?

    @Mirauder_Mo: Hab ich mir schon gedacht 😉

    @spk101: Cool, danke, das kompiliert ohne Fehler. Aber kann ich das jetzt auch richtig mit dem 2d-Index([][]) verwenden wie ein normales Array? Kann das jetzt nämlich nicht mal eben testen, das werde ich erst später merken.



  • Für sowas schreibt man sich am besten eine Klasse die das 2dim Array kapselt.



  • spl@t schrieb:

    @CarstenJ: Würd ich ja gerne, aber geht das in 2d? Und so, dass ich nicht oder nur unwesentlich mehr Speicher benötige?

    vector<vector<>> braucht nicht spürbar mehr als verschachteltes new[]. Viel besser wäre es unabhängig vom Container, einen eindimensionalen zu nehmen und die "Zweidimensionalität" dann selbst reinzuinterpretieren. Als Größe width * height nehmen und dann arr[y * width + x] statt arr[x][y] verwenden. Das spart vermutlich eine Menge Speicherverwaltungsinformationen.
    Dann ist der vector auch nur so minimal teurer als new[]/delete[], dass man nicht mehr viel gegen seine Vorteile sagen kann 😉



  • Hmpf, zu spät 😞
    Habs jetzt schon so gemacht:

    template <class T>
    class Array2d {
    public:
    	Array2d(int iquad) : _size(iquad)
    	{
    		_values = new T*[iquad];
    		for(int i =0; i < iquad; ++i)
    			_values[i] = new T[iquad];
    	}
    
    	~Array2d() 
    	{
    		delete[] _values;
    	}
    
    	const T&	getValue(int i, int j) const 
    	{
    		return _values[i][j];
    	}
    
    	void		setValue(int i, int j, T value) 
    	{
    		_values[i][j]=value;
    	}
    
    	const int getSize() const {return _size;}
    
    private:
    	T**	_values;
    	int _size;
    };
    


  • Die Klasse gibt im Destruktor nur das Zeigerarray frei, nicht die Werte. Außerdem gibt es Speicherfehler, wenn verschieden Array2ds zuweist oder kopiert -> Lieblingsbuch.

    Ich wäre für ungefähr das hier:

    template<typename T>
    class area
    {
        std::size_t width_, height_;
        std::vector<T> buf_;
    
    public:
        area(std::size_t width = 0, std::size_t height = 0, const T& init = T())
        : width_(width), height_(height), buf_(width * height, init)
        {
        }
    
        // Destruktor, Kopierkonstruktor und Zuweisungsoperator entfallen dank vector
    
        const T& operator()(std::size_t x, std::size_t y) const
        {
            return buf_[y * width_ + x];
        }
    
        T& operator()(std::size_t x, std::size_t y)
        {
            return buf_[y * width_ + x];
        }
    
        std::size_t width() const
        {
            return width_;
        }
    
        std::size_t height() const
        {
            return height_;
        }
    };
    

    Natürlich kann man da noch fleißig erweitern, aber das sollte schonmal sicherer und effizienter sein.



  • Es stimmt nicht das die Feldgröße zu Kompilezeit feststehen muss, folgendes funktioniert z.B.:

    void foo(int bar)
    {
      char test[bar];
    }
    

    Warum das bei Funktionen aber bei Klassen nicht geht ist mir ein Rätsel.



  • Wenn du die GCC-Erweiterungen abstellst, geht das auch in Funktionen nicht mehr. Das ist kein gültiges C++. Im aktuellen C-Standard (C99) hingegen geht das.



  • Das war mir auch klar, gibts aber irgendeinen logischen Grund warum man sowas in Funktionen benutzen kann aber in Klassen nicht 😕
    Mal von der Definition abgesehen...



  • Lars schrieb:

    Das war mir auch klar, gibts aber irgendeinen logischen Grund warum man sowas in Funktionen benutzen kann aber in Klassen nicht 😕

    Ich schätze es liegt daran: Die Größe einer Klasse sollte möglichst Compilezeit-konstant sein, sonst wird die Adressberechung von Variablen rekursiv und damit sehr kompliziert. Die Größe eines Activation Records einer Funktion muss dagegen nicht unbedingt konstant sein, da sie sich nicht rekursiv verschachteln lassen und damit die Adressen lokaler Variablen bei Anwesenheit von variablen Arrays immer noch relativ einfach berechnen lassen.



  • Eine Frage zu dem nützlichem Code von operator void:
    Gibt es andere Datenstrukturen der STL, die schneller beim Zugreifen auf ein n-tes Element sind? Denn das ist es was ich möglichst schnell machen muss (Durchhangeln von vorne nach hinten würde auch gehen), das Hinzufügen von Daten zum Beispiel kann ruhig entsprechend langsamer sein.



  • Hm, direkt schneller als ein vector bzw. roher Zeiger/rohes Array (läuft aufs selbe hinaus) ist eigentlich nichts - man kann sonst noch darauf achten, dass die Daten für den Prozessor immer gut zugreifbar liegen. Außerdem könnte ich mir vorstellen, dass ein Zugriff auf vector<int>-Elemente schneller geht als auf die eines vector<char>, weil die an 32bit-Grenzen ausgerichtet werden etc. usw. - aber einen einfachen "schnelleren Container" kann ich mir nicht vorstellen.



  • Hm, direkt schneller als ein vector bzw. roher Zeiger/rohes Array (läuft aufs selbe hinaus)

    Läuft absolut nicht aufs selbe hinaus.
    Es geht ja nicht nur um den Zúgriff. Auch das Instanzieren braucht seine Zeit.
    Die Containerklasse vector ist dabei nicht gerade als optimal anzusehen ...



  • Knallhart zum Thema beigetragen 👍



  • operator void schrieb:

    Knallhart zum Thema beigetragen 👍

    Musste nunmal sein, wenn hier so'n Müll erzählt wird.



  • @rofl: Du hast sicher recht, jedoch bezog sich (denk ich mal) operator voids Beitrag auf meine Frage, wo ich ausschließlich vom Zugriff sprach.
    Tja, wenn der Vector beim Zugriff genauso schnell ist wie ein normales Array (oder fast) dann kann die miese Performance (Prog läuft noch 1/3 so schnell wie vorher mit C) eigentlich nur noch an der Zuweisung eines Koordinaten-Objektes mit 3 Membervariablen und nachfolgenden Zugriffen darauf liegen. Das wird immerhin (zusammen mit dem Vector-Zugriff) ungefähr 3 000 000 mal pro Sekunde ausgeführt...



  • Ist die entsprechende Stelle klein genug, um sie hier kurz zu erklären? Wie Quellcode beim Übersetzen von C nach C++ dreimal langsamer wird interessiert mich doch irgendwie 🙂



  • hehe, sehr gern. Allerdings ist das nur eine ungefähre Schätzung. Vorher hatte ich eine Funktion, die jeden Frame (soll mal ein kleines Spiel werden) aufgerufen wurde und dabei 256² mal auf ein 256*256 C-Array zugegriffen hat.
    Dabei bekam ich ungefähr 100 FPS.
    Jetzt habe ich alles mögliche geändert, viele Klassen hinzugefügt ect.
    Zum Beispiel eine Terrain-Klasse mit Renderfunktion. Insgesamt bekomme ich damit noch 30 FPS. Die Funktion sieht so aus:

    void Terrain::Render() {
    	CVector3 currVert; //CVector3 ist eine Struktur mit 3 Floats um Koordinaten zu speichern
    	for(int x = 0; x < _koords.width(); ++x)
            { //koords ist das 2dArray (mit der Implementation von oben) mit vector<CVector3> 
    	   glBegin(GL_TRIANGLE_STRIP);
               for(int z = 0; z < _koords.width(); ++z)
               {
    		currVert = _koords(x, z);
    		         glColor3f(currVert._y/(16.0f*_scale),currVert._y/(32.0f*_scale),currVert._y/(64.0f*_scale));
    		glVertex3f(currVert._x, currVert._y, currVert._z);
    
    		currVert = _koords( (x+1)%_koords.width(), z);
    			glColor3f(currVert._y/(16.0f*_scale),currVert._y/(32.0f*_scale),currVert._y/(64.0f*_scale));
    			glVertex3f(currVert._x, currVert._y, currVert._z);
    
    			addPoly_count(2);
            }
    	    glEnd();
        }
    }
    

    Natürlich könnte das jetzt auch aufgrund anderer Änderungen langsamer geworden sein. Dazu habe ich jedoch die 2 Zeilen mit currVert = _koords (bla, bla); entfernt und glVertex und glColor einfach direkt immer 3 mal 0 übergeben. Dabei bekam ich wieder 100 FPS. Das heißt also, dass durch die anderen Änderungen auch etwas Performance verloren geht, denn ich hab ja vorher immerhin noch auf das c-Array zugreifen müssen (und dabei immer noch ein paar einfache Berechnungen ausgeführt), und jetzt ist es ganz ohne was immer noch nur genauso schnell. Aber damit kann ich leben. Deutlich wird ja, dass die Zuweisung, der Vector-Zugriff und die Zugriffe auf die Koordinatenklasse Zeit kosten.
    Noch was: Kompiliere ich mit Release, bekomm ich immerhin schon wieder 76 FPS. Wie viel die Optimierungen vorher bei der C-Version gebracht hätten, weiß ich aber nicht.


Anmelden zum Antworten