Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

10.01.2012, 13:54

3dimensionales dynamisches array

Hi,

Ich habe für mein Konsolenprojekt einen kleinen "GUIMgr" geschrieben (unfertig), der mehrere layers an characters abspeichern kann und pro character eine farbe für den text und hintergrund mitspeichert.
dafür hab ich ein dynamisches 3dimensionales array verwendet, bei dem ich mir jedoch nicht 100% sicher bin, ob ich es richtig lösche (also es funktioniert alles und wirft keine fehler, aber das muss ja nichts bedeuten).

Könnte mir vl jemand bestätigen, ob ich es richtig gemacht habe? Hier der Code - im Destructor lösche ich den Speicherplatz.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
 * GUIMgr.cpp
 *
 *  Created on: 10.01.2012
 *      Author: Simon
 */

#include "GUIMgr.hpp"
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <fstream>

GUIMgr::GUIMgr( int maxLayers ) :
        _maxLayers( maxLayers ), _width( 80 ), _height( 25 ), _outputHandle( GetStdHandle( STD_OUTPUT_HANDLE ) )
{
    _currentX = _currentY = 0;

    // allocate memory for the chararray and colorarray of each layer
    // dimension 1
    _chars = new char**[_width];
    _fgColors = new Color**[_width];
    _bgColors = new Color**[_width];

    // dimension 2
    for( unsigned int x = 0; x < _width; x++ )
    {
        _chars[x] = new char*[_height];
        _fgColors[x] = new Color*[_height];
        _bgColors[x] = new Color*[_height];
    }

    // dimension 3
    for( unsigned int x = 0; x < _width; x++ )
    {
        for( unsigned int y = 0; y < _height; y++ )
        {
            _chars[x][y] = new char[maxLayers];
            _fgColors[x][y] = new Color[maxLayers];
            _bgColors[x][y] = new Color[maxLayers];
        }
    }

    // initialize
    for( unsigned int x = 0; x < _width; ++x )
        for( unsigned int y = 0; y < _height; ++y )
            for( int z = 0; z < _maxLayers; ++z )
            {
                // if a character is zero, it won't me print to the console
                _chars[x][y][z] = 0;
                _fgColors[x][y][z] = FG_WHITE;
                _bgColors[x][y][z] = BG_BLACK;
            }
}

GUIMgr::~GUIMgr()
{
    // free dimension 3
    for( unsigned int x = 0; x < _width; x++ )
    {
        for( unsigned int y = 0; y < _height; y++ )
        {
            delete[] _chars[x][y];
            delete[] _fgColors[x][y];
            delete[] _bgColors[x][y];
        }
    }

    // free dimension 2
    for( unsigned int x = 0; x < _width; x++ )
    {
        delete[] _chars[x];
        delete[] _fgColors[x];
        delete[] _bgColors[x];
    }

    // free dimension 1
    delete[] _chars;
    delete[] _fgColors;
    delete[] _bgColors;
}

// prints a .txt file on the specific layer and stores the characters
// of the .txt file in the layer's char array
void GUIMgr::printFile( const std::string & txtFilePath, int layer )
{
    std::string line;
    std::ifstream myfile( txtFilePath );
    _currentX = 0;
    _currentY = 0;
    if( myfile.is_open() )
    {
        while( myfile.good() )
        {
            getline( myfile, line );
            unsigned int tmp = _currentY;
            printString( line, _currentX, _currentY, layer );
            if( tmp == _currentY )
            {
                // no line break because input line was to short (<80 characters)
                // make the breakline "per hand"
                _currentY++;
            }
            _currentX = 0;
        }
        myfile.close();
    }
}

void GUIMgr::printString( const std::string & text, int x, int y, int layer, Color foreground, Color background )
{
    if( x >= 80 || x < -1 || y >= 25 || y < -1 )
        return;

    if( x != -1 && y != -1 )
    {
        // coordinates specified -> set new cursor position
        _currentX = x;
        _currentY = y;
    }

    _COORD c;
    c.X = _currentX;
    c.Y = _currentY;
    SetConsoleCursorPosition( _outputHandle, c );
    SetConsoleTextAttribute( _outputHandle, foreground | background );

    // print the text char by char
    size_t pos = 0;
    while( pos < text.size() )
    {
        const char c = text.at( pos );
        _chars[_currentX][_currentY][layer] = c;
        _fgColors[_currentX][_currentY][layer] = foreground;
        _bgColors[_currentX][_currentY][layer] = background;
        std::cout << c;

        _currentX++;
        if( _currentX >= _width )
        {
            // string is longer then console width -> stop printing the string
            _currentX = 0;
            // go to the next line
            _currentY++;
            if( _currentY >= _height )
            {
                // if we reach the end of the console -> return to line zero
                _currentY = 0;
                _COORD coord;
                coord.X = _currentX;
                coord.Y = _currentY;
                SetConsoleCursorPosition( _outputHandle, coord );
            }
            break;
        }
        ++pos;
    }
}

// resets a layer to its default values
void GUIMgr::clearLayer( int layer )
{
    if( layer < 0 || layer >= _maxLayers )
        return;

    for( unsigned int x = 0; x < _width; ++x )
        for( unsigned int y = 0; y < _height; ++y )
        {
            _chars[x][y][layer] = 0;
            _fgColors[x][y][layer] = FG_WHITE;
            _bgColors[x][y][layer] = BG_BLACK;
        }
}

// reprints all characters except the specified layer's characters
void GUIMgr::hideLayer( int layer )
{
    if( layer < 0 || layer >= _maxLayers )
        return;

    _COORD c;
    c.X = 0;
    c.Y = 0;

    // clear the console
    clear();

    // reprint all except the specified layer
    for( unsigned int x = 0; x < _width; ++x )
        for( unsigned int y = 0; y < _height; ++y )
            for( int z = 0; z < _maxLayers; ++z )
            {
                if( z == layer || _chars[x][y][z] == 0 )
                    continue;
                c.X = x;
                c.Y = y;
                SetConsoleCursorPosition( _outputHandle, c );
                SetConsoleTextAttribute( _outputHandle, _fgColors[x][y][z] | _bgColors[x][y][z] );
                std::cout << _chars[x][y][z];
            }
}

void GUIMgr::printAll()
{
    _COORD c;
    c.X = 0;
    c.Y = 0;
    for( unsigned int x = 0; x < _width; ++x )
        for( unsigned int y = 0; y < _height; ++y )
            for( int z = 0; z < _maxLayers; ++z )
            {
                // ingore "ZERO" character
                if( _chars[x][y][z] == 0 )
                    continue;
                c.X = x;
                c.Y = y;
                SetConsoleCursorPosition( _outputHandle, c );
                SetConsoleTextAttribute( _outputHandle, _fgColors[x][y][z] | _bgColors[x][y][z] );
                std::cout << _chars[x][y][z];
            }
}

// clears the entire console but doesn't clear the chars and colors stored
// in each layer's char/color array
void GUIMgr::clear()
{
    _COORD c;
    c.X = 0;
    c.Y = 0;
    SetConsoleTextAttribute( _outputHandle, FG_BLACK | BG_BLACK );
    SetConsoleCursorPosition( _outputHandle, c );
    for( unsigned int x = 0; x < _width; ++x )
        for( unsigned int y = 0; y < _height; ++y )
            std::cout << ' ';
}

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

2

10.01.2012, 16:32

Was spricht gegen einen einfachen std::vector?
Du musst dir nur eine eigene at-Methode schreiben, die die Position im Vector berechnet und schon hast du den ganzen Stress mit dem Freigeben und Reservieren nicht.
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

10.01.2012, 17:24

Vor allem isses auch effizienter als ein Jagged Array ;)

4

10.01.2012, 18:07

Ich habe für mein Konsolenprojekt einen kleinen "GUIMgr" geschrieben (unfertig), der mehrere layers an characters abspeichern kann und pro character eine farbe für den text und hintergrund mitspeichert.
dafür hab ich ein dynamisches 3dimensionales array verwendet, bei dem ich mir jedoch nicht 100% sicher bin, ob ich es richtig lösche (also es funktioniert alles und wirft keine fehler, aber das muss ja nichts bedeuten).

Das hört sich schon vom Design her schon sehr komisch an. Wieso hat jedes Zeichen eine Vorder- und eine Hintergrundfarbe? Ich würde auch viel eher eine Klasse für ein Zeichen machen, dass dann Zeichen, Vordergrundfarbe und Hintergrundfarbe speichert, so hast du schonmal nur noch 1 Array, statt 3. Und dann eine Layerklasse, die sich umd ie verwaltung von Layers kümmert.


Wenn du dein Programm besser strukturierst, hast du imemr nur wenige Variablen auf einmal, so das du weniger übersehen kannst. Außerdem musst du dein Programm nicht überall ändern, sobald du ein Detail änderst. Wenn du schon weißt, was new und delete macht, aber trotzdem nicht beurteilen kannst, was dein Programm hier eigentlich macht, spricht schon einiges dafür, dass es schlicht und einfach zu kompliziert und undurchschaubar programmiert wurde.

Selbst wenn weitere Klassen keine zusätzliche Funktionalität implementieren würde, würden sie immer noch die Lesbarkeit und Wartbarkeit deines Programmes verbessern. Also vergiss das 3D Array (es sei denn, du hast einen sehr guten Grund, den du hier noch nicht erläutert hast).

Achja: Pro Zeichen 2 Farben zu speichern, hört sich einfach nur falsch an. Wie bunt soll den die GUI werden, oder wie oft willst du die selbe Farbe speichern?
Lieber dumm fragen, als dumm bleiben!

5

11.01.2012, 07:44

Zitat

Wieso hat jedes Zeichen eine Vorder- und eine Hintergrundfarbe?

die windows konsole ist 80x25 "felder" groß (oder waren es 24?) und pro feld kann ich eine eigene farbe für den text und eine eigene farbe für den hintergrund wählen. das kann man durchmischen wie man will und ist nicht beschränkt. da dies möglich ist, wird es auch unterstützt. ob ich es dann verwende um regenbogen augenkrebstext zu erzeugen oder nicht, ist ja eigentlich egal. es ist schön zu wissen, wenn man alle möglichkeiten offen hat.

Zitat

Ich würde auch viel eher eine Klasse für ein Zeichen machen, dass dann Zeichen, Vordergrundfarbe und Hintergrundfarbe speichert

ist bereits im folgeschritt schon passiert. habe die 3 dinge einfach in ein struct gepackt und dann von diesem struct das array erzeugt

Zitat

Wenn du schon weißt, was new und delete macht, aber trotzdem nicht beurteilen kannst, was dein Programm hier eigentlich macht,

würde ich so nicht sagen. ich habe nur noch nie mit einem dynamischen 3d array gearbeitet und wollte nur auf nummer sichen gehen, ob der speicher richtig freigegeben wird. das scheinte aber zu passen aka ich habe es eh richtig gemacht,

Zitat

Was spricht gegen einen einfachen std::vector?
Du musst dir nur eine eigene at-Methode schreiben, die die Position im Vector berechnet und schon hast du den ganzen Stress mit dem Freigeben und Reservieren nicht.

wieso muss ich mir eine eigene at methode schreiben? ich kann doch einen vector<vector<vector<struct>>> erzeugen und da vector den [] operator unterstützt, könnte ich ihn doch nahezu ident wie ein 3d array benutzen? frage ist für mich weiterhin, ob es sich lohnt.
wenn ich den 3d index umrecne, dass ich ein 1d array habe bzw. nur einen vector ists klar. dann liegt das alles schön hintereinander im speicher und ist dadurch schneller. ist dies aber auch bei verschachtelten vectoren der fall?

6

11.01.2012, 11:48

Nein, natürlich nicht. Ein Vektorobjekt hat immer ein konstante Größe. Intern hat er irgendwo einen Speicher auf den Datenbereich. Du hast außen also einen Vektor mit vielen Zeigern drinnen, jeder Zeiger zeigt wieder auf einen Vektor mit vielen Zeigern und da sind dann irgendwo die Elemente hinter. Sie liegen also ziemlich wirr verteilt im Speicher rum. Wie genau das intern aussieht, kommt vermutlich auf die Speicherverwaltung an, aber einen einzigen, Großen Vektor zu haben, sollte schneller und einfacher sein.
Du kannst mittels Proxy Objekten auch immernoch über [x][y][z] auf deine Elemente zugreifen, auch wenn sie in einem großen Vektor liegen. Musst du dir halt nur entsprechend programmieren.
Lieber dumm fragen, als dumm bleiben!

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

7

11.01.2012, 15:23

Ich sprach auch nicht von einem verschachtelten Vector sondern von einem einzigen mit der größe breite*höhe*tiefe. Das ist wesentlich effizienter. Ein verschachtelter vector macht nur Sinn, wenn die "Zeilen" verschiedene längen haben sollen/können.

An die Proxyobjekte hab ich garnicht gedacht. Ist ja eigentlich auch egal ob Methode oder Proxyobjekt. :D
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

8

11.01.2012, 16:29

Ok, auf der Konsole macht ein 2D Textarray schon Sinn, aber ich würde dann zumindest für Layer eine eigene Klasse schreiben, so dass du ein 1D Array (oder Liste, oder was anderes) von Objekten hast, die intern ein 2D Array verwalten. Alleine, wenn man mal ein Layer rauslöschen oder einfügen will (am besten noch an beliebiger Stelle) wird es sonst sehr kryptisch und man kommt mit den Bezeichnern vollkommen durcheinander.
Lieber dumm fragen, als dumm bleiben!

9

12.01.2012, 11:33

hab es soweit mal provisorisch jetzt beendet mit der vector variante. ich denke, dass ich für die konsolenanwendungen auch nicht mehr brauche und auf die optimierte performance mit 1d array verzichte (vorerst zumindest).
proxyobjekte hab ich noch nie gehört, aber scheint mir (was ich kurzfristig gegoogeld habe) auch etwas übertrieben zu sein für meinen anwendungsfall.

Wen's interessiert, oder wer sichs anschauen mag und feedback geben will (kommentare sind noch spärlich und variablenbezeichnungen oftmals vl auch noch nicht perfekt).

GUIMgr.hpp
GUIMgr.cpp
Test.cpp

Was macht es bisher:
  • Speichert bis zu 3 layers an zeichen + farben ab
  • print filepath methode gibt ein .txt file aus, wie es im .txt file drinsteht. beginnt bei koordinate (0,0)
    ist bei mir z.b. interessant, um menues, dialoge, etc. in txt files auslagern zu können
  • print string x,y gibt einen string aus beginnedn bei koordinate x,y. schießt man über den konsolenrand hinaus wird in der nächsten zeile wieder korrekt eingerückt. geht der text über die komplette konsole hinaus, wird das, was nicht mehr platz hat, ignoriert.
    ist ein wort prinzipiell zu lang, wird es gekürzt dargestellt, wenn möglich (also mit einem . am ende), oder, wenn es in der nächsten zeile komplett platz hätte in die nächste zeile gerückt).
    leading whitespaces in einer neuen zeile werden ignoriert. fixe zeilenumbrüche mittels \n möglich. multiple whitespaces werden zu einem zusammengefügt.
  • print string x,y,width,height gibt text in einer box aus. funktionalität wie print string x,y.
  • createborder erzeugt einen rahmen

hier die ausgabe des testszenarios:

(Link)

10

12.01.2012, 11:53

Die sind schon ein wenig trickreich, aber eigentlich recht einfach.
http://www.globalguideline.com/interview…xy_objects_in_C

Im Prinzip muss der [] Operator ja schon einen Wert zurückgeben. Er gibt hier aber einfach ein anderes Objekt zurück, auf das man wieder den [] Operator anwenden kann, dadurch erhält man die Schreibweise mit b[x][y]. Stattdessen kann man auch einfach eine Funktion b.get(x, y) schreiben, was aber halt nicht ganz so nett aussieht. Proxy Objekt heißt es deshalb, weil es nur als Rückgabewert dieser einen Funktion auftaucht und man damit nichts anderes machen kann als den [] Operator aufzurufen. Es ist so gesehen gar kein richtiges Objekt, sondern nur ein Hilfskonstrukt, um die nettere Schreibweise hinzukriegen. Proxyobjekte gibt es auch in anderen zusammenhängen, z.B. kann man damit das explicit keyword für Konstruktoren emulieren.
Das ist nicht wirklich wichtig, und man muss es nicht tun, aber es schadet nichts, solch netten Tricks im Hinterkopf zu behalten.
Lieber dumm fragen, als dumm bleiben!

Werbeanzeige