C++ Vector Array

CarotinBombe

Grünschnabel
Hallo liebe Community,

ich bin neu hier und würde mich über eure Hilfe freuen! :) Mit C++ habe ich erst angefangen und möchte gern ein kleines Programm schreiben namens "Farm". Es sollen dort mittels Arrays Tiere verwaltet werden. Man kann sich Slots kaufen um am Ende Tiere kaufen zu können. Soweit so gut. Das Problem ist, dass das Programm zwar den Array (.resize) verändern kann, jedoch erkennt das Programm den Array als 0. Somit können keine Slots ausgegeben werden in der while-Schleife (Bei If (Auswahl == 1). Im Endeffekt hat man noch keine Slots(Die muss man sich erst "kaufen"). Dabei wird ja wie ich sagte der Array slots verändert. Ich glaube es liegt daran, dass es sich um keinen dynamischen Datentyp handelt? Ich kenne mich wie gesagt nicht gut aus und würde mich freuen, wenn Ihr mir helfen könntet. Code ist noch nicht vollendet. Nicht wundern ;)

C++:
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
    int z = 0;
    int i = 0;
    vector<int> slots(z);

    while ( i < 1 )
    {
    system("cls");
    int auswahl;

    cout << "Meine Farm\n";
    cout << endl;
    cout << "1.    Meine Tiere\n";
    cout << "2.    Tiere kaufen\n";
    cout << "3.    Tiere verkaufen\n";
    cout << "4.    Meine  Slots\n";
    cout << "5.    Exit\n" << endl;;
    cout << "Auswahl: "; cin >> auswahl;

  
    if(auswahl == 1)
    {
        system("cls");
        int t = 0;
        while ( t < slots.size())
        {
            cout << t << ". Slot    ";

            if (slots[t] == 1)
            {
                cout << "Kuh\n";
            }

            else if (slots[t] == 2)
            {
                cout << "Schwein\n";
            }

            else if (slots[t] == 3)
            {
                cout << "Huhn\n";
            }

            else
            {
                cout << "LEER\n";
            }
            t++;
        }
        system("pause");
    }

    if (auswahl == 2)
    {


    }

    if (auswahl == 3)
    {


    }

    if(auswahl == 4)
    {
        bool buy = false;
        string key;
        system("cls");
        cout << "Sie besitzen " << slots.size() << " Slots." << endl;
        cout << "Möchten Sie welche kaufen?";
        cout << " J/N?";
        cin >> key;

        if (key == "j" | key == "J")
        {
        int value = 0;
        cout << "Wie viele möchten Sie kaufen?";
        cin >> value;
        z = z + value;
        slots.resize(z);
        return main();
        }

        if (key == "n" | key == "N")
        {
            return main();
        }
    }

    if (cin.fail())
    {
        cout << "Eingabe Fehlerhaft!\n";
        system("pause");
    }
    }
    return 0;
}
 

cwriter

Erfahrenes Mitglied
Hi

Es sollen dort mittels Arrays Tiere verwaltet werden.
Eins vorweg: Ein Vektor ist kein Array, sondern ein Array auf Steroiden.
Das Problem ist, dass das Programm zwar den Array (.resize) verändern kann, jedoch erkennt das Programm den Array als 0.
Du meinst die Grösse (.size()) ist 0? Du initialisierst das ja so, mit z == 0.
Somit können keine Slots ausgegeben werden in der while-Schleife (Bei If (Auswahl == 1). Im Endeffekt hat man noch keine Slots(Die muss man sich erst "kaufen").
Genau.

Dabei wird ja wie ich sagte der Array slots verändert.
Ja, allerdings wird nur die Grösse verändert, und der Wert der neuen Elemente ist irgendwas (hier wahrscheinlich 0).

Also zur Erläuterung:
Ein Array sieht generell so aus:
C++:
int arr[arraysize];
Die Grösse (arraysize) ist fix und kann nicht geändert werden. Tatsächlich ist dieser Code semantisch äquivalent zu:
C++:
int arr_0;
int arr_1;
int arr_2;
//...
int arr_(arraysize-1);

Du nutzt den Vektor. Ein Vektor holt sich seinen Speicher von einem anderen Bereich (siehe Stack vs Heap) und kann sich daher in der Grösse verändern. Gleichzeitig ist ein Vektor als FILO-Struktur mit Random Access gedacht, also eine Datenstruktur, bei der First In Last Out gilt (wie bei den Tellerstapeln in der Mensa), wo die du Werte aber auch innerhalb ansehen (aber nicht entfernen) kannst.

Du benutzt resize() hier, was aber eigentlich nicht die einfachste Variante ist:
push_back() erhöht die Grösse des Vektors um 1 und fügt ein Element hinzu.
resize() erhöht (oder verkleinert) die Grösse des Vektors auf die gegebene Grösse und setzt alle neuen Werte auf einen Standardwert.
reserve() erhöht die Grösse des Speichers, ohne den Vektor selbst zu vergrössern. Das ist praktisch, denn so kosten einzelne push_back() weniger. Das wird aber schon sehr kompliziert.

Für dich: Nutze push_back(). resize() und reserve() sind meistens Optimierungen, die später erlernt werden sollten.

Ich glaube es liegt daran, dass es sich um keinen dynamischen Datentyp handelt?
Was ist ein dynamischer Datentyp?
(Es gibt diese Bezeichnung, aber sie bezieht sich auf Klassen und deren Vererbung, nichts, was du hier benutzt).
Falls du "in der Grösse veränderlich" meinst: Ein Array hat eine fixe Grösse, ein Vektor nicht.

Dann zu deinem Code generell:
Deine returns sind seltsam.
return main(); ruft main() nochmals auf, und gibt dann davon den Wert zurück. Du meintest wohl "return 0;" (oder einen anderen int-Wert). Oder, falls du einfach direkt den Loop wiederholen willst: "continue;"
Ein logisches Oder wird in C "||", nicht "|" geschrieben (2 senkrechte Striche verwenden).
Ein "|" gibt es zwar auch, ist dann aber das binäre Oder (ist ähnlich und funktioniert auch, aber nicht typesafe. Nutze "||").
Dein while(i < 1) ist (im Moment) gleich wie while(true). Aber das ist wohl eine deiner Baustellen.

Der Rest des Codes sollte für deine Zwecke reichen und ist für eine(n) Anfänger(In) nicht schlecht.
Weiter so!

Gruss
cwriter
 

CarotinBombe

Grünschnabel
Cwriter,

ich danke dir sehr für deine Hilfe. Scheinbar habe ich wirklich einiges verwechselt und falsch aufgefasst. :confused:
Vektor und Array waren für mich das selbe.... danke für die Erklärung!

Also heißt das im Klartext resize verändert die Größe des Vektors und löscht allen deren einzelnen Inhalt? Mit reserve() wird wohl der Speicher einzelner Vektoren erhöht um mehr speichern können?
Frage ist nun... wie kann man das lösen. Es ist ja wie ein Stall den ich habe. In den Stall sind Slots für Tiere frei. Man kann welche kaufen um Tiere in den Stall zu setzten (Slots). Im Endeffekt möchte ich den Vektor ja vergrößern um einen bestimmten Wert? Dabei soll der eigentliche Inhalt der bestehenden Vektoren ja bestehen bleiben. Mit resize() baue ich ja so zu sagen, alle Ställe neu. Also wenn ich 5 Ställe besitze und 5 wieder kaufe habe ich logischerweise 10. Im Endeffekt besitze ich dann ja 10 leere Ställe, obwohl in den vorherigen 5 schon Tiere gelebt haben. Ich hoffe du verstehst was ich meine...

Man könnte vllt. mittels push_back() mittels For-schleife um den Wert vergrößern den man möchte. Also gebe ich z.B. 5 ein. Dann kann die For-Schleife solange push_back() wiederholen bis eine Variable i = 5 ist, oder? Gäbe es da keine andere Möglichkeit? Oder habe ich das jetzt falsch aufgefasst ?

Ich habe den Code angepasst und überarbeitet. Jetzt habe ich das kleine Problem, das ich keine Tiere kaufen kann. Ich bin natürlich dran, es soweit selbst zu lösen.
C++:
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
    int z = 0;
    vector<int> slots(z);

    while (true)
    {
    system("cls");
    int auswahl;

    cout << "Meine Farm\n";
    cout << endl;
    cout << "1.    Meine Tiere\n";
    cout << "2.    Tiere kaufen\n";
    cout << "3.    Tiere verkaufen\n";
    cout << "4.    Meine  Slots\n";
    cout << "5.    Exit\n" << endl;;
    cout << "Auswahl: "; cin >> auswahl;

   
    if(auswahl == 1)
    {
        system("cls");
        int t(1);
        cout << "Meine Tiere\n\n";

        for (size_t q = 0; q < slots.size(); ++q)
        {
            cout << t << ". Slot        ";

            if (slots[q] == 1)
            {
                cout << "Kuh\n";
            }

            else if (slots[q] == 2)
            {
                cout << "Schwein\n";
            }

            else if (slots[q] == 3)
            {
                cout << "Huhn\n";
            }

            else
            {
                cout << "LEER\n";
            } 
            t++;
        }
        system("pause");
    }

    if (auswahl == 2) 
    {
        int kaufen;
        system("cls");
        cout << "Tiere kaufen\n\n";
        cout << "1.Kuh    / 2.Schwein    / 3.Huhn\n" << endl;
        cin >> kaufen;

        if (kaufen == 1 && slots.size() > 0)
        {
            for (int frei = 0; slots[frei] < 1; frei++)
            {
                slots[frei] = kaufen;
            }
            cout << "Kauf erfolgreich!";
            system("pause");
        }

    }

    if (auswahl == 3) 
    {


    }

    if(auswahl == 4) 
    {
        string key;
        system("cls");
        cout << "Sie besitzen " << slots.size() << " Slots." << endl;
        cout << "Möchten Sie welche kaufen?";
        cout << " J/N?";
        cin >> key;

        if (key == "j" || key == "J")
        {
        int value = 0;
        cout << "Wie viele möchten Sie kaufen?";
        cin >> value;
       
        for (int fr = 0; fr < value; fr++)
        {
            slots.push_back(0);
        }
        continue;
        }

        if (key == "n" || key == "N")
        {
        continue;
        }
    }

    if (cin.fail())
    {
        cout << "Eingabe Fehlerhaft!\n";
        system("pause");
    }
    }
    return 0;
}
 
Zuletzt bearbeitet:

sheel

I love Asm
Noch etwas mehr dazu:
Vektor und Array waren für mich das selbe
Sie haben den selben Sinn: Mehrere Werte von einen Variablentyp speichern.

Technisch ist ein Vektor aber ein Klassenobjekt (Einzelvariable), bei dem alles mit Funktionsaufrufen darauf passiert. zB. push_back usw. Und auch [nummer] beim Vektor ist letztendlich ein Funktionsaufruf wie .get(nummer), nur "getarnt":
Der Vektor hat dann "in sich" wieder ein normales Array, auf das man aber nur über die Funktionen des Vektors zugreifen kann.

Der Grund, dann überhaupt einen Vektor zu verwenden, sind eben die Funktionen: zB. ein Array größer machen ist eigentlich ziemlich umständlich; aber das kann einem egal sein, weil es in push_back/resize/usw. schon fertig reinprogrammiert ist. Anderes Beispiel, man kann bei einem rohen Array keine size() abfragen. Wenn man die haben will muss man immer eine zweite Variable zum Array dazu haben, in der man die Anzahl der Elemente mitspeichert. Auch das macht Vektor für einen. Usw.

...

Zu resize usw., das hast du nicht ganz richtig verstanden.
Zuerst einmal, wie bereits gesagt hat der Vektor in sich ein Array, das er aber komplett selbst verwaltet, inkl. Vergrößerungen usw. wenn nötig. Wenn man bei einem Vektor mit zB. 3 Elemente drin auf [0] [1] und [2] zugreift und die ändert usw., passiert mit der Vektorgröße (natürlich) nichts.
Wenn man jetzt mit push_back ein paar neue Elemente zum Vektor addet erhöht sich die Größe für jedes push_back um 1 (es gibt auch das Gegenteil, ein Element löschen, aber das ist vorerst egal). Weil sich die Größe erhöht erhöht sich auch der Wert, den man von size() bekommt. Soweit vermutlich klar.

Jetzt ist das Vergrößern vom (inneren) Array aber eine umständliche Angelegenheit, die noch dazu zeitlich relativ langsam und speicherhungrig ist: Zb. muss jedes Element im aktuellen Array einmal kopiert werden. Bei einem Array mit 1 Milliarde ELementen ... für jedes einzelne push_back 1 Milliarde Elemente kopieren wäre blöd. Vektor macht das daher nicht so: Obowhl man mit push_back nur ein Element addet werden gleich mehrere neue Plätze geschaffen, zB. gleich eine Million zu der 1 Millarde dazu. Ein Platz von der Million wird dann mit dem Wert von push_back gefüllt, und die anderen einfach vorerst in Reserve gehalten. Für die nächsten 999999 push_backs kann man sich die volle Arrayvergrößerung dann sparen, es muss nur noch der Wert an die passende Stelle getan werden. (Bei einem Vektor mit 10 Elementen wird natürlich keine Million dazu gemacht, sondern zB. wieder 10, zusammen eben 20. Sinnvolle Werte ermittelt der Vektor eben anhand der derzeitigen Arraygröße).
Damit hätte man größe Teile vom Geschwindigkeitsproblem gelöst ... aber es gibt da noch das kleine Problem, size(). Man erwartet sich eigentlich, dass ein push_back immer 1 erhöht, nicht manchmal eine Million und manchmal gar nichts (wenn noch freie Reserveplätze vorhanden waren). size() erhöht sich aber tatsächlich immer um 1, einfach weil man vom Vektor "angelogen" wird. Bzw. es wurde ja auch nie behauptet, dass size() die tatsächliche innere Arraygröße ergibt ... size() speichert mit, wie viel man selber eingefügt hat. Egal was drin passiert (was ja eigentlich der Sinn vom Vektor ist: Man soll sich eben nicht darum kümmern müssen, wie Arrayvergrößerungen funktionieren und schnell sind usw.)

So, und wenn das geklärt ist sind resize und reserve nicht mehr schwer zu verstehen:

resize() mit einem höheren Wert als size() ist das Selbe wie mehrere push_back (zumindest, wenn man einen Wert angibt. Ohne nimmt es einen Defaultwert, zB. 0 für int). Also so viele Einfügungen, dass man die Wunsch-Elementanzahl erreicht (das innere Array darf größer sein, egal).

resize() mit einem niedrigeren Wert als size() löscht ein paar Elemente, um die Wunsch-size zu erreichen. (Im Zweifelsfall heißt das wieder: Innen speichern dass diese Stellen frei sind == size entsprechend anpassen, aber die Plätze im inneren Array dürfen noch existieren. Natürlich)

Und reserve ist ganz was Anderes, wo es diesmal tatsächlich um die innere Arrayvergrößerung geht (obwohl Vektor einem das Nachdenken darüber ja großteils ersparen will): Wenn man von einem leeren Vektor weg, der mit einem inneren Array mit zB. 10 Plätzen startet, dann 1 Milliarde Elemente mit resize einfügt, wird das innere Array von 10 auf (mehr als) 1 Millarde vergrößert, klar.
Wenn man statt resize aber 1 Millarde Elemente mit push_back einfügt (zB. weil weil eben nicht alle Millarden Werte gleich sind, resize nimmt ja immer den selben Wert), weiß der Vektor am Anfang noch nicht dass das Ziel 1 Millarde ist, und deswegen werden insgesamt mehr als eine Arrayvergrößerung gemacht: zB. wenn 10 voll ist auf 20, wenn 20 voll sind zB. auf 50 usw. ... gleich nach den ersten 10 eine weitere Millarde reservieren wäre für die meisten Anwendungsfälle ja ziemlich blöd; aber wenn man so wirklich so viele Elemente einfügen will wirds durch die stufenweise Vergrößerung wieder langsamer als nötig.
Mit reserve kann dem Vektor jetzt einfach sagen "Nur damit du es weißt, ich will eine Millarde Elemente, beachte das beim Vergrößern".
Vor den push:back einmal aufrufen, dann hat man eine einzelne Arrayvergrößerung von 10 auf Millarden, und kann das dann nach und nach mit push_back füllen.
 

cwriter

Erfahrenes Mitglied
Also wenn ich 5 Ställe besitze und 5 wieder kaufe habe ich logischerweise 10. Im Endeffekt besitze ich dann ja 10 leere Ställe, obwohl in den vorherigen 5 schon Tiere gelebt haben. Ich hoffe du verstehst was ich meine...
Keine Angst: resize() behält alte Werte bei, die werden nicht einfach so gelöscht (bzw. intern werden sie kopiert und gelöscht, aber das geht hier wieder zu weit).
Im Endeffekt möchte ich den Vektor ja vergrößern um einen bestimmten Wert?
Genau das macht push_back.
Also Beispiel:
C++:
std::vector<int> vec;
//vec ist leer.
vec.push_back(42);
//vec[0] == 42
vec.push_back(1);
//vec[0] == 42
//vec[1] == 1
vec.push_back(321);
//vec[0] == 42
//vec[1] == 1
//vec[2] == 321
? Mit reserve() wird wohl der Speicher einzelner Vektoren erhöht um mehr speichern können?
Sheel's Antwort erläutert das recht gut: Reserve macht nur intern Platz und ist eine Optimierung, aber für ein semantisch korrektes Programm nicht notwendig.

Man könnte vllt. mittels push_back() mittels For-schleife um den Wert vergrößern den man möchte. Also gebe ich z.B. 5 ein. Dann kann die For-Schleife solange push_back() wiederholen bis eine Variable i = 5 ist, oder? Gäbe es da keine andere Möglichkeit? Oder habe ich das jetzt falsch aufgefasst ?
Doch, genau resize() tut das. Wenn du ohnehin alles zuerst auf 0 setzen willst, kannst du resize(newsize, 0) verwenden. Allerdings kannst du dann die einzelnen Werte nicht direkt unterschiedlich bestimmen (hier bekommen alle neuen Werte den Wert 0), sondern müsstest sie nachträglich ändern. Geht auch, keine Frage.

Ich habe den Code angepasst und überarbeitet. Jetzt habe ich das kleine Problem, das ich keine Tiere kaufen kann. Ich bin natürlich dran, es soweit selbst zu lösen.
Ok, dann sage ich dazu mal nichts (ausser, dass das Problem bei Zeile 68 und folgende liegt).

Gruss
cwriter
 

CarotinBombe

Grünschnabel
Danke danke euch für eure große Mühe!
Soweit habe ich alles glaube ich verstanden.

Ich sitze immer noch an meinen Problem fest mit der Schleife. Vielleicht könntet Ihr mir doch ein kleinen Gedankenanstoß geben.
C++:
if (kaufen == 1 && slots.size() > 0)
            {
                for (int frei = 0; slots[frei] < 1; frei++)
                {
                    slots[frei] = 1;
                }
                cout << "Kauf erfolgreich!";
                system("pause");
            }

Die For-Schleife geht alle Slots ab bis ein Slot den Wert = 0 (was bedeutet der Slot(Stall) ist leer und da kann ein Tier drin "wohnen"). Also ich kaufe mir 5 Slots und alle haben automatisch den Wert = 0. Somit wenn ich schon Tiere habe ist deren Wert ungleich 0 oder größer 1 zum Beispiel slots[0] = 1 (Kuh) . Leider bricht mein Programm da ab. In dem Abschnitt soll das Programm automatisch leere Lücken füllen.Also wenn Slot 1 - 4 mit Tieren besetzt ist, soll der 5 Slot ja automatisch als 0 erkannt werden und dort die "Kuh" zum Beispiel unterbringen. Ich habe verschiedene Lösungen versucht... aber entweder es klappt nicht oder es stürzt ab.
 

cwriter

Erfahrenes Mitglied
Ich sitze immer noch an meinen Problem fest mit der Schleife. Vielleicht könntet Ihr mir doch ein kleinen Gedankenanstoß geben.
Gerne :)

Die For-Schleife geht alle Slots ab bis ein Slot den Wert = 0 (was bedeutet der Slot(Stall) ist leer und da kann ein Tier drin "wohnen").
Kleine Korrektur: Du suchst, bis ein Wert < 1 ist (-1 könnte z.B. auch gelten).

Also ich kaufe mir 5 Slots und alle haben automatisch den Wert = 0.
Korrekt.

Leider bricht mein Programm da ab.
Abbruch heisst Absturz?


Also wenn Slot 1 - 4 mit Tieren besetzt ist, soll der 5 Slot ja automatisch als 0 erkannt werden und dort die "Kuh" zum Beispiel unterbringen.
Hier liegt dein Denkfehler.
Gehen wir es durch, mit 5 freien Plätzen.
0 0 0 0 0

Dann kommst du und kaufst ein Tier.
(das 1 sollte wohl irgendein Tier sein, würde ich nicht als Konstante (Kuh) machen).
C++:
for(int frei = 0; slots[frei] < 1; frei++)
Heisst: Du beginnst mit frei == 0.
slots[0] ist 0, also 0 < 1.
Dann setzt du den Slot auf 1, wir haben also
1 0 0 0 0

Frei wird um 1 erhöht, also ist es jetzt 1.
slots[1] ist 0, also 0 < 1.
Nächster slot auf 1 gesetzt. Und so weiter.
Dann ist frei irgendwann mal 4 und du erhöhst frei um 1, also 5. Und jetzt greifst du auf slots[5] zu. Aber Ups: slots ist nur 5 Elemente gross, und du greifst auf das 6. zu -> Absturz.

Ich fände in der Bedingung eigentlich das Gegenteil logischer: >= 1 (oder halt != 0).
Dann wäre es: Solange der Platz besetzt ist, schau den nächsten an und entscheide dann weiter. Ist dann einer frei, dann fülle nur einen einzigen. (Dazu findest du quasi zuerst die Position im for-loop und setzt dann den Wert in die gefundene Position, falls diese innerhalb des Vektors liegt).
Jetzt ist es: Solange es freie Plätze hat, fülle sie (mit immer derselben Tierart).

In beiden kannst du das Problem damit beheben, einfach die Bedingungen zu erweitern:
C++:
for(size_t frei = 0; frei < slots.size() && slots[frei] == 0; frei++)
size_t ist einfach ein unsigned integer Typ (nicht gleich unsigned int).


Gruss
cwriter
 
Zuletzt bearbeitet:

CarotinBombe

Grünschnabel
Gerne :)


In beiden kannst du das Problem damit beheben, einfach die Bedingungen zu erweitern:
C++:
for(size_t frei = 0; frei < slots.size() && slots[frei] == 0; frei++)
size_t ist einfach ein unsigned integer Typ (nicht gleich unsigned int).

Danke danke :) Jedoch habe ich jetzt das Problem, das alle meine Slots mit Kühen besetzt ist. Im Endeffekt will ich ja nur 1x Kuh kaufen. Das macht es dann ja etwas schwieriger. Ich habe schon einiges versucht in der for-Schleife - auch enthalten mit einer If-Anweisung. Egal wie ich es mache. Endweder ich habe gar keine Kühe, oder ich habe nur Kühe. Dabei soll z.B. bei 10 Slots die frei sind ja nur 1 Slot besetzt werden.
 

cwriter

Erfahrenes Mitglied
Im Endeffekt will ich ja nur 1x Kuh kaufen.
Dachte ich mir schon ;)

Das macht es dann ja etwas schwieriger.
Dafür müsstest du, wie schon gesagt, umgekehrt suchen:
Zuerst musst du mit einem Loop eine freie Stelle finden, und dann nur diese Stelle füllen.

Etwa so:
C++:
size_t index;
for(index = 0; index < slots.size() && slots[index] != 0; index++);
//Nun ist index entweder == slots.size() (-> kein Platz vorhanden) oder ist die erste freie Position
if(index == slots.size()) {} //kein freier Platz
else slots[index] = TIER;

Gruss
cwriter