C++ Funktion für 2D vector ?

T_55

Lieutenant
Registriert
Feb. 2013
Beiträge
643
Hallo,

mal eine totale Anfängerfrage zum Thema Funktionen. Kann ein Vector auch flexibel von einer Funktion direkt bearbeitet werden mit den üblichen Funktionen der c++ STL wie zB resize, push_back etc.?

Zum Beispiel hier ist ein globales Array was dann in der Funktion irgenwie verarbeitet wird:

Code:
//global
std::vector<std::vector<int>> IchBinEinFeld;

int main()
{
   funktion();
}

void funktion()
{
   // Und dann wird mit dem Array irgendwie gearbeitet:
   Beispielfeld.resize(5);
   Beispielfeld[0].resize(10)
   Beispielfeld[0][0] = 987;
   Beispielfeld[0].push_back(1234);
   // usw
}


Die Frage wäre jetzt, wenn es noch weitere Felder gibt die von der gleichen Funktion verarbeitet werden sollen, wie würde das dann gehen? Zum Beipiel gibt es 5 Felder:

Code:
std::vector<std::vector<int>> AAA;
std::vector<std::vector<int>> BBB;
std::vector<std::vector<int>> CCC;
std::vector<std::vector<int>> DDD;
std::vector<std::vector<int>> EEE;

Und alle diese sollen durch die gleiche Funktion verarbeitet werden, wie bekomme ich das hin?

lg
 
Ja, das geht im Grunde so wie Du möchtest.

T_55 schrieb:
Die Frage wäre jetzt, wenn es noch weitere Felder gibt die von der gleichen Funktion verarbeitet werden sollen, wie würde das dann gehen? Zum Beipiel gibt es 5 Felder:
Code:
std::vector<std::vector<int>> AAA;
std::vector<std::vector<int>> BBB;
std::vector<std::vector<int>> CCC;
std::vector<std::vector<int>> DDD;
std::vector<std::vector<int>> EEE;
und alle diese sollen durch die gleiche Funktion verarbeitet werden, wie bekomme ich das hin?

Ich fürchte, ich verstehe nicht ganz. Ist die gleiche Funktion auch die selbe Funktion? Meinst Du: per Parameter in die Funktion übergeben?
Code:
 void funktion(std::vector<std::vector<int>>& FELD)
{
 ...
}
Hmmm ...
 
blöderidiot schrieb:
Code:
 void funktion(std::vector<std::vector<int>>& FELD)
Genau das habe ich gesucht! :) super Danke


Noch eine kleine Frage, wenn man bei vector ausversehen einen Wert in einem Feld aufruft zB [2000][3000] den es gar nicht gibt weil die Felder viel kleiner sind dann bekomme ich ein bösen Fehler beim debuggen. Gibt es da eine einfache Lösung damit das Programm nicht abstürzt?

lg
 
Das wird übrigens nicht funktionieren, weil std::vector::operator[] in dem Fall keine Exception wirft, sondern undefiniertes Verhalten hervorruft. Will man eine Exception, muss man die at-Methode benutzen.

...das ist allerdings die Art von Exception, die normalerweise nie auftreten sollte und in der Regel auch nicht abgefangen wird. Wenn sowas durch User-Input passieren kann, lieber vorher validieren:
Code:
if (index < feld.size())
  mach_was(feld[index]);
else
  zeige_fehlermeldung();

Und falls mal irgendwo ein Out of Range-Error geworfen wird, der nicht so geplant war, dann macht es in aller Regel auch keinen Sinn, da großartig irgendwas abzufangen. Ist das Programm vernünftig geschrieben, wird es einigermaßen kontrolliert abstürzen.
 
Zuletzt bearbeitet:
Ok danke, mir ist aufgefallen, dass wenn man std::vector<std::vector<int>> var; hat und das nicht resized wird, dann die zweite Dimension den Wert 4294967168 hat. Die erste hat 0 aber die zweite Dimension egal welcher Index immer 4294967168. var[0].size()
Zum Prüfen ob genug Daten da sind ist das dann schlecht weil die Logik würde mir ja sagen es sind genug Daten da weil statt 0 der Wert 4294967168 rausgezogen wird.
Ergänzung ()

-
Ach jetzt hab ich es, das passiert nur wenn die erste Dimension = 0 ist. Heißt also man muss erst die erste Dimension auf 0 prüfen und wenn diese nicht 0 ist dann kann man die zweite Dimension prüfen.
 
Das Problem ist wahrscheinlich auch hier schon, dass var[0] garnicht existiert? Oder gilt ganz sicher
assert(var.size() >= 1);?
Vektoren verhalten sich eigentlich immer genau so wie man ganz normalen denkend erwarten würde und wenn man so einen Wert zurück bekommt hat man einen Fehler gemacht.
Du kannst dich übrigens nicht darauf verlassen, dass dein Programm abstürzt wenn du auf einen out-of-range index zugreifst. Wahrscheinlich ist das hier auch das Problem.
push_back vergrößert den Vektor evtl mehr als du denkst aber size() liefert immer die exakte Zahl an Elementen zurück die auch drin ist. Der Hintergrund ist, dass 100x push_back nicht zu 100x verschieben und neu allokieren von Speicher führen soll. Das wird dadurch realisiert, dass der reservierte (.reserve()) Speicher nicht nur um +1 erhöht wird sondern idR verdoppelt wird. Das hat den Vorteil das man nur log(N) Speicheroperationen durchführt für N push_back Aufrufe. Wenns dich wirklich interessiert ist hier ein im Browser lauffähiges Beispiel: http://www.cplusplus.com/reference/vector/vector/reserve/

Ich würde dir aber einfach raten: Benutz einen Vektor einfach wie ein intelligentes array was sich selbstständig um Größe und Speicher kümmert und schreib das Programm einfach wie du denkst - das klappt in der Regel auch. Wenn komische Fälle auftreten liegts bestimmt nich am Vektor sondern an einem Programmierfehler.

Das Problem mit dem var[0] könntest du prüfen indem du .at(0) ausprobierst. Wahrscheinlich fliegt dann die schon erwähnte Exception.
 
Ja weil im Prinzip nur std::vector<std::vector<int>> var; gesetzt ist, wurde noch keine Größe der Felder definiert. Ich dachte solange ich keine Größe definiere wird auch kein unnötiger Speicher belegt allerdings hab ich mir damit eben die Anfälligkeit auf so einen OutOfRange-Error eingefangen.

Ich hab eine Funktion A mit der die Felder gefüttert werden und so mit Werten belegt werden. Bevor die Felder gefüttert werden ist die Größe noch nicht definiert weil ich im Vorfeld kein Speicherplatz verschwenden will weil eventuell manache Felder gar nicht befüllt werden.
Dann gibt es noch eine andere Funktion B die den Inhalt der Felder verarbeiten soll sofern genug Inhalt vorhanden ist. Und genau das ist der Knackpunkt die Funktion B greift im schlechten Falle auf Felder zu die noch gar nicht ausreichend gefüttert wurden. Eigentlich müsste die Funktion B erst warten bis Funktion A genug Daten gefüttert hat.

Ich hab nochmal neu getestet:

Code:
    std::vector<std::vector<int>> var;
    std::cout << "1D = " << var.size() << std::endl;
    std::cout << "2D = : " << var[0].size() << std::endl;
    system("pause");

Das Problem ist es gibt so einen Fehler, ich kann also so die Funktion B nicht prüfen lassen ob genug Felder zur Verarbeitung vorhanden sind... :(
 
solange ich keine Größe definiere wird auch kein unnötiger Speicher belegt
Da du hier ja nur mit sehr wenigen Feldern arbeitest würd ich mir da keine Gedanken machen. Wenns irgendwann mal sehr viele werden kannst du statt vector eine map verwenden aber auch das würde ich erstmal nicht tun.

Was ist denn genau das Problem? Was ist dein 'var' eigentlich? Wenn du eine Art quadratisches Spielfeld willst, mach das ruhig. Erstell zB für Schach (8*8 Felder) deinen vector der 8x vector der jeweiligen Länge 8 enthält.

Ansonsten müsstest du etwas mehr Infos drumrum liefern was du eigentlich vor hast damit man dir besser helfen kann.

Ich hab eine Funktion A mit der die Felder gefüttert werden und so mit Werten belegt werden.
Was genau soll das heißen? Du meinst es gibt eine Funktion A die das Feld aufbaut? Die Felder werden kaum diese Funktion bekommen (=damit gefüttert werden). Oder was meinst du damit?
Es spricht nichts dagegen erst eine Funktion zu schreiben, die das Feld 'initialisiert' (=8 vectoren in var einfügt und diese sinnvoll mit werten belegt).
Außerdem solltest du unbedingt sprechende Namen verwenden.
var, funktionA, funktionB sagen nicht aus, was dort passiert.
 
Zuletzt bearbeitet:
Die Größen der Dimensionen sind im Vorfeld nicht fix sondern ergeben sich aus dem Programmablauf.
Zum Beispiel [0][22] dann [1][10000] dann [2][0] dann [3][50] usw. Jetzt für alle zweiten Dimensionen 10000 Felder in den Speicher zu schreiben nur weil [1][10000] diesen als maximalwert hat wäre doch ziemliche Platzverschwendung.
Wie lang die Felder sind entscheidet das Programm nach nicht vorhersehbaren Faktoren. Das ist der Grund warum ich vorher die Feldlängen nicht fest fixiere weil sie variieren.
 
Es ist ein wenig esoterisch zu sagen wie man Fehlerfälle abfängt ohne zu wissen was Funktion B tun soll.

Sagen wir deine Funktion B will an die Daten an den Indizes x und y ran. Dann kannst du bevor du var[x][y] überhaupt aufrufst prüfen ob die existieren:
Code:
  void FunktionB( std::vector<std::vector<int>>& var, int xIndex, int yIndex ) {
  //wir sollte noch prüfen ob xIndex, yIndex positiv sind, aber nehmen wir mal an dass sie es sind

  //wenn die linke Seite falsch ist, versucht das Programm gar nicht erst die rechte Seite zu prüfen
  if( xIndex < var.size() && yIndex < var[xIndex].size() ) {
    int Ergebnis = TuEtwas( var[x][y] );
  }

}
 
Ich mache es jetzt einfach so, ich stelle sicher das vor dem Zugriff auf die Felder die Feldanzahl der ersten Dimension festgelegt ist weil nur dann liefert auch die zweite Dimension den Wert 0 beim aufruf der size-feldgröße und stürzt nicht ab. In der Funktion die auf die Felder zugreifen soll kann ich dann mit size checken ob genug vorhanden sind.
 
Etwas konkreterer Code waere schoen. Dann waere es auch einfacher, Empfehlungen zu geben. Mir kommt zum Befuellen der Felder beispielsweise std::generate in den Sinn. Allgemein lohnt es sich beim Umgang mit Containern immer wieder n Auge auf die Algorithmen der STL zu werfen. Dazu nach Moeglichkeit immer von Iteratoren Gebrauch machen. Mit diesen beiden Massnahmen kann man diese harten Indexzugriffe mit dem []-Operator schon extrem minimieren, was deinen Code erheblich robuster macht.
 
Die Größen der Dimensionen sind im Vorfeld nicht fix sondern ergeben sich aus dem Programmablauf.
Zum Beispiel [0][22] dann [1][10000] dann [2][0] dann [3][50] usw. Jetzt für alle zweiten Dimensionen 10000 Felder in den Speicher zu schreiben nur weil [1][10000] diesen als Maximalwert hat wäre doch ziemliche Platzverschwendung.
Wie lang die Felder sind entscheidet das Programm nach nicht vorhersehbaren Faktoren. Das ist der Grund warum ich vorher die Feldlängen nicht fest fixiere weil sie variieren.
Sind deine Felder denn immer gefüllt von 0 bis zur jeweiligen Größe? Oder hat dein Feld evtl Lücken?
Wie schon mal gesagt: Bei Lücken aber auch sonst könnte es Sinn machen eine map zu verwenden. Diese hat den operator[] der entweder den Inhalt zurückliefert wenn er existiert ODER aber diesen in dem Moment an dieser Stelle erstellt.
Der größte Nachteil ist O(log N) statt O(1) im Zugriff pro Dimension, da der Computer über den Index erst im Baum die Stelle des jeweiligen Elements finden muss anstatt wie beim vector die Stelle des Elements direkt zu kennen. Ein großer Vorteil ist aber das man auch mit Lücken solche Felder mit weniger Speicher verwalten kann. Wenn du C++11 verwendest (oder QT mit QHash) und wenn du nicht über mehrere Elemente einer Dimension in ihrer Reihenfolge iterieren musst gibt es auch die unordered_map welche idR nur O(1) Zugriffszeit hat je Dimension aber trotzdem die anderen Vorteile der map bietet.
 
Ja also es gibt in dem Sinne keine Lücken sondern es gibt immer ein Anfang und ein Ende. Entweder würde ich die Felder direkt per index befüllen oder wenn sie wachsen dann mit push_back immer neue Werte dranzuhängen. Das mit den Algos der STL und auch map werde ich mir nochmal genau anschauen, denke aber mit vector ist es Prinzipiel schon mal möglich da es immer eine zusammenhängende Reihe bzw eine wachsende Reihe ist.
Danke nochmal für den Input
lg
 
Zurück
Oben