Originally Posted by
Benniczek
Still, that's not the reason for the random values in the array that appear only when getline() is called. Has somebody an idea why this happens?
No, it is the reason: you are looking at the effects of undefined behaviour. If the dynamic array has already been destroyed, then accessing its contents results in undefined behaviour. So, the memory corresponding to the "random values in the array" could have been overwritten because they are supposed to no longer be in use, and by attempting to access the non-existent dynamic array, you take a peek into that overwritten memory.
Compile and run this modified version of your code to see that it has nothing to do with getline per se:
Code:
#include <iostream>
#include <string>
using namespace std;
class Item {
public:
string name;
string description;
Item() {}
Item(const string new_name, const string new_description) {
name = new_name;
description = new_description;
}
};
Item item01("sword", "A sharp and deadly weapon.");
Item item02("shield", "This will protect you. To a certain extent.");
Item item03("stick", "What is this for exactly?");
Item item04("bottle of water", "A bottle full of refreshing spring water.");
class invSpace {
public:
Item* item;
int quantity;
invSpace() {
item = NULL;
quantity = 0;
}
};
class Inventory {
private:
invSpace* spaces = NULL;
size_t size;
public:
int free_space() {
int free = 0;
for (int i = 0; i < size; i++) {
if (spaces[i].item == NULL) {
free++;
cout << i << " = free" << endl;
}
else {
cout << spaces[i].item << " / " << spaces[i].quantity << endl;
}
}
return free;
}
Inventory() {}
Inventory(size_t new_size) {
size = new_size;
spaces = new invSpace[size];
/*for (int i = 0; i < size; i++) { //makes no difference, just for testing
spaces[i] = invSpace();
}*/
}
~Inventory() {
delete[] spaces;
}
};
class Player {
public:
string name;
Inventory inventory;
Player(const string new_name) {
inventory = Inventory(40);
name = new_name;
}
};
Player player("Me");
int main() {
string input;
for (int i = 0; i < 20; ++i) {
input = std::string('a', i);
}
player.inventory.free_space();
}
You will probably get similiar "random values". If you don't, no big deal: that's also an effect of undefined behaviour.
Now compile and run this fixed version of your code:
Code:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class Item {
public:
string name;
string description;
Item() {}
Item(const string new_name, const string new_description) {
name = new_name;
description = new_description;
}
};
Item item01("sword", "A sharp and deadly weapon.");
Item item02("shield", "This will protect you. To a certain extent.");
Item item03("stick", "What is this for exactly?");
Item item04("bottle of water", "A bottle full of refreshing spring water.");
class invSpace {
public:
Item* item;
int quantity;
invSpace() {
item = NULL;
quantity = 0;
}
};
class Inventory {
private:
invSpace* spaces = NULL;
size_t size;
public:
int free_space() {
int free = 0;
for (int i = 0; i < size; i++) {
if (spaces[i].item == NULL) {
free++;
cout << i << " = free" << endl;
}
else {
cout << spaces[i].item << " / " << spaces[i].quantity << endl;
}
}
return free;
}
Inventory() {}
Inventory(size_t new_size) {
size = new_size;
spaces = new invSpace[size]();
}
Inventory(const Inventory& other) : size(other.size) {
spaces = new invSpace[size];
std::copy(spaces, other.spaces, size);
}
Inventory& operator=(const Inventory& other) {
Inventory temp(other);
swap(temp);
return *this;
}
~Inventory() {
delete[] spaces;
}
void swap(Inventory& other) {
using std::swap;
swap(spaces, other.spaces);
swap(size, other.size);
}
};
class Player {
public:
string name;
Inventory inventory;
Player(const string new_name) {
inventory = Inventory(40);
name = new_name;
}
};
Player player("Me");
int main() {
string input;
//Inventory inventory(40); //no error if declared outside ouf player class
while (1) {
cout << "\n>> ";
getline(cin, input);
if (input == "x") {
break;
}
else {
player.inventory.free_space();
}
}
}
Actually, although it allowed you to observe this "random values" problem, note that this constructor implementation is rather inefficient:
Code:
Player(const string new_name) {
inventory = Inventory(40);
name = new_name;
}
My demonstrated fix involved implementing the copy assignment operator for Inventory, but in reality you don't need to copy any Inventory objects here. You could use move assignment, but a more directly efficient solution is to use the Player constructor's initialiser list:
Code:
Player(const string new_name) : name(new_name), inventory(40) {}
Of course, doing this also means that there is no temporary Inventory object whose dynamic array is destroyed, hence even if you do not implement the copy constructor and copy assignment operator, the "random values" will no longer happen, although as long as you have such incomplete RAII implementation, a disaster is waiting to happen.