c++ initialize non pointer object again - c++

In my header file I have:
class Game
{
private:
string _name;
Level _currentLevel;
public:
Game();
~Game();
void setName();
void run();
};
In my cpp file I have my run function:
void Game::run()
{
bool finished = false;
string input;
while (!finished)
{
// get input
std::cout << "Enter a command: \n";
std::getline(std::cin, input);
if (input == "quit")
{
finished = true;
}
else if (input == "new")
{
Level _currentLevel;
}
else if (input == "print")
{
_currentLevel.printMap();
}
else
{
std::cout << "Unknown command! \n";
}
}
}
constructor and printmap method of Level
Level::Level()
{
_width = RandomGenerator::Instance()->getRandom(6, 10);
_height = RandomGenerator::Instance()->getRandom(6, 10);
for (int y = 0; y < _height; y++)
{
for (int x = 0; x < _width; x++)
{
addRoom(x, y);
}
}
}
void Level::printMap()
{
for (int y = 0; y < _height; y++)
{
for (int x = 0; x < _width; x++)
{
if (x != 0)
cout << " - ";
cout << _map[coordinate(x, y)].getSize();
}
cout << "\n";
}
}
However when I type new, that runs Level _currentLevel; (to create a new non pointer object), the object dosnt change. I can see it dosn't change the values of level when I run printmap (which prints a map with 30 random values created in the Level constructor). While debugging the value of _height changes in the Level constructor. How should the value of _currentLevel be updated from my Game class?

Your new block creates a local stack variable that happens to have the same name as your instance variable (_currentLevel). It does not overwrite the instance variable, and that's why nothing changes.
You have a few straightforward choices:
Use a pointer. I suggest using a shared_ptr so you don't have to worry about deallocating memory on your own.
Extend Level to have an Initialize function. The constructor can call this, or you can call it from other code later if you want to re-initialize an existing variable.
Copy a new local variable to the instance variable.
Personally, I'd suggest the pointer, but either works.

For starters the prototypes should be in the header and the implementation in the source file. Apart from that, in game::run you declare a second local _currentlevel which shadows the class variable. Outside the constructor, you never modify the class field.
Replace the shadowing line with this->_currentlevel = Level();

Related

The object a pointer is pointing to changes values every time a get function is called? C++

I have two classes, one for fuel and the other for miles. Every 24 miles you use 1 fuel. For this assignment I am required to have a pointer in my "miles" class to point to a "fuel" class so we can have "mile" class work with the "fuel" class without having to create a whole object "fuel" object inside the "mile" class. It was going well until I tested it and after some debugs it seems that, for some reason, the fuel object has it's values changed whenever I call the get function in my "miles" class. However, all I am doing is returning a member variable. Why would it change my fuel object? I also do not change the pointer's value anywhere else so it should always point to the fuel object.
Class Specification
#ifndef ODO_H
#define ODO_H
#include "FuelGauge.h"
class Odometer
{
private:
int currentMiles = 0;
int maxMilage = 999999;
FuelGauge* fuelObject = nullptr;
int counter = 0;
public:
Odometer(int, FuelGauge);
int getMiles();
void addMiles();
};
#endif ODO_H
Class Implementation
#include "Odometer.h"
Odometer::Odometer(int currMileage, FuelGauge Gauge) {
if (currMileage >= 0 && currMileage <= maxMilage) {
currentMiles = currMileage;
}
else {
cout << "Starting mileage is invalid" << endl;
}
fuelObject = &Gauge;
}
int Odometer::getMiles() {
return currentMiles;
}
void Odometer::addMiles() {
if (currentMiles < maxMilage) {
currentMiles++;
counter++;
}
else if (currentMiles >= maxMilage) {
cout << "You have reached the max mileage ... resetting to zero!" << endl;
currentMiles = 0;
}
if (counter >= 24) {
fuelObject->useFuel();
counter = 0;
}
}
In main, when I call the getMiles() function; The object pointed to by "fuelObject" has it's values changed to some random large number. Why does this happen?
This should be undefined behavior right here.
Odometer::Odometer(int currMileage, FuelGauge Gauge) {
if (currMileage >= 0 && currMileage <= maxMilage) {
currentMiles = currMileage;
}
else {
cout << "Starting mileage is invalid" << endl;
}
fuelObject = &Gauge;
}
Once your constructor finishes execution, your fuelObject is left pointing to garbage, since the parameter will go out of scope and it is by-val.
Apart from that, I have to agree with #scheff that the error you are seeing is likely not caused by the code you have shown.

How do you delete both a pointer in a vector of pointers of Objects and the Object itself?

I'm trying to code an text-based adventure game builder. I have three classes: Room, Object, and my main class. In my Room class, I have a (private) vector of pointers of Objects: vector<Object*> objectsInRoom
This keeps track of all the Objects stored in each room. I have a function called objects() in the Room class that returns objectsInRooms for when I call that vector in my main class.
vector<Object*> Room::objects() { return objectsInRoom; }
In my main class, in my function pickUpObject(), I've created a vector of pointers of Objects called roomObject. I call objects() in the Room class and store the Objects in objectsInRoom (which is only accessed in the Room class) in roomObject (which is accessible in my function in main). I also have a vector of Objects called allObjects that stores all the items that I want to pick up from the room and carry around with me. It has a global scope.
I want to make it so that if I pick up an item in a particular room, I add the item to allObjects, delete the pointer to that element in roomObjects (and thus the pointer to that element in objectsInRooms in the Room class), and the item itself.
My pickUpObject function is: (Room* current just tells me what room I'm in and thus what Objects I have)
void pickUpObject(vector<Object>&allObjects, Room* current)
{
vector<Object*>roomObjects; int length; string name; char flag;
roomObjects = current->objects();
length = roomObjects.size();
bool repeat = true;
while (repeat)
{
if (length == 0)
{
cout << "There are no objects to pick up in " << current->name() << endl;
repeat = false;
}
else
{
cout << "There is a ";
for (int k = 0; k < roomObjects.size() - 1; k++)
{
cout << roomObjects[k]->getName();
if (length > 2)
cout << ", ";
}
if (length > 1)
cout << " and " << roomObjects[length - 1]->getName() << " here." << endl;
else
cout << roomObjects[length-1]->getName() << "." << endl;
cout << "What object do you want to pick up?" << endl;
cin >> name;
//this is where the deletion happens
for (int i = 0; i < length; i++)
if (name.compare(roomObjects[i]->getName()) == 0)
{
allObjects.push_back(*roomObjects[i]);
roomObjects.erase(roomObjects.begin() + i);
deleteVectorContent(roomObjects, i, i + 1);
}
cout << "roomObject size = " << roomObjects.size() << endl;
cout << "--------------------" << endl;
cout << "allObject size = " << allObjects.size() << endl;
for (int i = 0; i < allObjects.size(); i++)
cout << allObjects[i].getName() << endl;
for (int i = 0; i < roomObjects.size(); i++)
{
cout << roomObjects[i]->getName() << endl;
}
cout << "Do you want to pick up another object? (Y/N): ";
cin >> flag;
if (flag == 'N')
repeat = false;
}
}
}
I've looked up various posts on StackOverflow to try and resolve my dilemma. In main, I've created a method called deleteVectorContent to try and delete the pointer.
void deleteVectorContent(vector<Object*> objectVector, int start, int stop)
{
for (int k = start; k < stop; k++)
delete objectVector[k];
objectVector.clear();
}
I've also tried 'roomObjects.remove()' to remove the item itself from that room. Whenever I compile, however, my compiler also throws me an exception. Help would be greatly appreciated.
P.S. The link to this assignment is here. If you scroll down to the "Extra Credit for the Programming Assignment" and go to the first one marked "10 points," that is what I am working on. Thank you so much for the help!
Room::objects() is returning a copy of objectsInRoom, so any modifications that pickUpObject() makes to that returned vector will not be applied back to objectsInRoom. You would need to make Room::objects() return a reference to objectsInRoom instead, eg:
vector<Object*>& Room::objects()
{
return objectsInRoom;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
vector<Object*> &roomObjects = current->objects();
...
}
Otherwise, don't provide direct access to objectsInRoom at all. Introduce new methods to Room to access/remove a given Object* from its objectsInRoom, eg:
int Room::numObjects()
{
return objectsInRoom.size();
}
Object* Room::getObject(int index)
{
return objectsInRoom[index];
}
Object* Room::takeObject(int index)
{
Object *obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
int length = current->numObjects();
...
for (int i = 0; i < length; ++i)
{
if (name == current->getObject(i)->getName())
{
Object *obj = current->takeObject(i);
allObjects.push_back(*obj);
delete obj;
break;
}
}
...
}
Note that allObjects is receiving copies of the actual Objects, not Object* pointers. The code you showed is leaking memory when you make a copy of *roomObjects[i] and then erase() the Object* at i without delete'ing the Object it is pointing at. If Object is so easily copyable, you can save yourself a lot of headaches by simply getting rid of all the Object* pointers and just use Object everywhere:
class Room
{
vector<Object> objectsInRoom;
...
};
int Room::numObjects()
{
return objectsInRoom.size();
}
Object& Room::getObject(int index)
{
return objectsInRoom[index];
}
Object Room::takeObject(int index)
{
Object obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object> &allObjects, Room* current)
{
int length = current->numObjects();
...
for (int i = 0; i < length; ++i)
{
if (name == current->getObject(i)->getName())
{
allObjects.push_back(current->takeObject(i));
break;
}
}
....
}
Otherwise, don't mix Object with Object* like you are, use Object* everywhere.
If you have a fixed set of Objects for the game, I would create a global vector<Object> to hold them all, and then just pass around Object* pointers everywhere as needed. Then you don't have to worry about cleaning up memory manually at all:
vector<Object> allGameObjects;
// fill as needed...
void Room::addObject(Object *obj)
{
objectsInRoom.push_back(obj);
}
Object* Room::takeObject(int index)
{
Object *obj = objectsInRoom[index];
objectsInRoom.erase(objectsInRoom.begin()+index);
return obj;
}
void pickUpObject(vector<Object*> &allObjects, Room* current)
{
...
allObjects.push_back(current->takeObject(i));
...
}
If you absolutely need a vector that owns Object* pointers that have to be cleaned up before the vector is destroyed, consider using vector<unique_ptr<Object>> for that, let the compiler and STL handle the hard work for you. If you ever find yourself having to write something like deleteVectorContent(), rethink your design.

Why does returning a data structure rather than a pointer mess with the integrity of my data?

I'm building a sparse matrix class that holds two arrays (row and column) of pointers to doubly linked lists (down and right). Sort of like this:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
Both arrays are initialized to have nullptr in every space until something is inserted in that place.
I have a function "readFile" that reads in objects from a text file and inserts them into this sparse matrix. For some reason, before this function returns, all of the data in it is fine, but after I return, I get random memory locations in my arrays. Here is main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"
using namespace std;
class basic
{
private:
int x, y;
string word;
basic *down;
basic *right;
public:
basic(int x, int y, string word)
{
this->x = x;
this->y = y;
this->word = word;
down = nullptr;
right = nullptr;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
basic *getRight()
{
return right;
}
void setRight(basic *newRight)
{
right = newRight;
}
basic *getDown()
{
return down;
}
void setDown(basic *newDown)
{
down = newDown;
}
void print()
{
cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
}
};
sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);
int main()
{
cout << "Working:\n\n";
sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
workingMatrix->printyArray();
cin.get();
cout << "Not working:\n\n";
sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
brokenMatrix.printyArray();
cin.get();
delete workingMatrix;
}
sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> matrix(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix.insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix.printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
sparseMatrix<basic> *readFile(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix->insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix->printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
and here is sparseMatrix.h:
template <class dataType>
class sparseMatrix
{
private:
//The dimensions of the sparse matrix.
int width;
int height;
//Dynamic array of pointers to heads of linked lists.
dataType** xArray;
dataType** yArray;
public:
//Constructor. Sets everything in the two arrays to nullptr.
sparseMatrix(int height, int width)
{
this->width = width;
this->height = height;
xArray = new dataType*[width];
yArray = new dataType*[height];
for (int row = 0; row < height; row++)
{
this->yArray[row] = nullptr;
}
for (int col = 0; col < width; col++)
{
this->xArray[col] = nullptr;
}
}
//Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
//all of those. Then when it's done, it deletes the two dynamic arrays.
~sparseMatrix()
{
dataType *currentdataType;
dataType *next;
for (int row = 0; row < height; row++)
{
currentdataType = yArray[row];
while (currentdataType != nullptr)
{
next = currentdataType->getRight();
delete currentdataType;
currentdataType = next;
}
}
delete [] yArray;
delete [] xArray;
}
//Creates a copy of the data we are passed, then creates links to this copy.
void insert(dataType data)
{
//Make sure the data is valid.
if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
{
std::cout << "That dataType doesn't fit into the sparse matrix!\n";
data.print();
std::cin.get();
}
else
{
//Copy the data we were passed.
dataType *newData = new dataType(data);
//Easy case. If nothing is in this row, set yArray[row] to the address of this data.
if (yArray[data.getY()] == nullptr)
{
yArray[data.getY()] = newData;
}
//Not so easy case. Move forward (right) until we find the right location, then set links.
else
{
dataType *current = yArray[data.getY()];
while (current->getRight() != nullptr)
{
current = current->getRight();
}
current->setRight(newData);
}
//Easy case. If nothing is in this col, set xArray[col] to the address of this data.
if (xArray[data.getX()] == nullptr)
{
xArray[data.getX()] = newData;
}
//Not so easy case. Move forward (down) until we find the right location, then set links.
else
{
dataType *current = xArray[data.getX()];
while (current->getDown() != nullptr)
{
current = current->getDown();
}
current->setDown(newData);
}
}
}
void printyArray()
{
for (int r = 0; r < height; r++)
{
if (yArray[r] != nullptr)
{
std::cout << r << ' ';
//yArray[r]->print();
}
}
}
};
readFile reads everything in from a file that looks like this:
0 0 hello
5 2 world
6 8 foo
9 5 bar
...
As expected, before returning, the only locations that are NOT nullptr are the ones that I have inserted into. (0, 2, 8 and 5). However when the function returns, EVERY SINGLE location in the array is not nullptr. I added a second function which returns a pointer to dynamically allocated sparseMatrix object, rather then returning the object itself, and this fixed it. However, I don't understand why. It seems like these two functions should behave identically the same way.
Also, the part that is most confusing to me, why does this run perfectly fine in Xcode, but not in Visual Studio?
tomse's answer is correct and gives the why and a fix, but it's an unnecessarily expensive fix for this problem. His suggestion of the copy constructor also solves numerous future problems such as the classics Why did my vector eat my data? and Dude, where's my segfault? Make the copy constructor. Don't use it unless you have to.
I think Andras Fekete got the problem right, but his post is kind of garbled. His solution is bang on, though.
Define your function like this:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
Remove the definition of matrix inside the function in favour of the one passed in.
Return false on error so you know the matrix is bad (or use exceptions).
Create the matrix in the calling function and pass it into the revised reader function.
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
That puts you right back where you were with the pointer version, but without the pointer and without having to do the extra work of copying data you didn't need to copy.
Your function:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns a copy of the object (which is OK), but sparseMatrix does not define a copy constructor, so the default generated will be used which creates a shallow copy by just copying the adresses inside the returned object.
But the memory where the address points to is deleted when you leave your function (because the destructor of the locally created object is called).
To solve this you have to define your own copy contructor in sparseMatrix which copies all the content of the object.
sparseMatrix(const sparseMatrix& rhs) :
width(rhs.width),
height(rhs.height),
xArray(nullptr),
yArray(nullptr)
{
... and now copy all the content from rhs.xArray to this->xArray,
(same for yArray)
}
The problem is that you're allocating 'matrix' inside both of the readFile functions. Upon returning from the function, both variables are deallocated. However, returning the value (eradFile) the matrix is copied into your variable of the calling function, whereas returning the pointer (readFileBROKEN) is just returning the address where the matrix used to be stored.
To fix this, you should allocate the 'matrix' variable, and pass in a reference to the function. Then the function can return a void while stuffing the matrix properly.

Casting objects in vector in c++

I know this question has already been asked, but none of the answers that I have as of yet found seem to suffice. I am creating a vector of pointers to a base class and adding all sorts of derived classes to it. Now, the base class has a virtual function that is overridden in all of the derived classes and that is unique to each of them. So, when I go through the vector and retrieve those objects and call the function on that object, I need it to call the right one, but all it will do is call the base class version. I am even trying to cast the individual elements back to their original class when I retrieve them from the vector but they refuse to be cast! e.g.
vector<base*> myBase;
DerivedClass *myDerived = static_cast<DerivedClass> myBase[i];
This doesn't work, despite the fact that everything I've read suggests that it should. My debugger says that despite all of this, myDerived is still of type base and it's version of my virtual function is being called.
Any ideas?
class BankAccount {
public:
BankAccount(string namein, string typein){
name = namein;
type = typein;
balance = 0;
}
virtual string getType();
virtual void printTransactions() = 0;
virtual int withdraw(double amt){
return getBalance() -amt;
}
};
class SavingsAccount: public BankAccount {
public:
SavingsAccount(string namein, string typein);
void addTransaction(string transType, string name);
virtual int withdraw(double amt);
void printTransactions();
virtual string getType();
private:
};
SavingsAccount::SavingsAccount(string namein, string typein): BankAccount(namein, typein) {
}
int SavingsAccount::withdraw(double amt){
double aBal = getBalance() - amt;
if (aBal > 0){
setBalance(aBal);
}
return getBalance() - amt;
}
class CheckingAccount: public SavingsAccount {
public:
CheckingAccount(string nameIn, string typein): SavingsAccount(nameIn, typein){
}
virtual int withdraw(double amt);
void printTransactions();
string getType(){
return "Checking";
}
};
int CheckingAccount::withdraw(double amtIn){
double newBal = getBalance() - amtIn;
if (newBal < 500.00 && newBal > 2.49) {
setBalance(newBal - 2.50);
}
return newBal;
}
int main(int argc, const char * argv[])
{
vector<BankAccount*> myAccts;
SavingsAccount *mySav;
CD *myCD;
CheckingAccount *myCheck;
switch (option) {
case 1: {
string name;
string type;
cout << "Enter name: ";
cin >> name;
getline(cin, dump);
cout << "Enter account type: ";
cin >> type;
getline(cin, dump);
if (type.compare("Checking") == 0) {
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
}
case 3:{
for (int x = 0; x < myAccts.size(); x++) {
if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
if (type == "Savings") {
mySav = static_cast<SavingsAccount*>(myAccts[x]);
double y = mySav->withdraw(amt);
if (y < 0){
cout << "Insufficient funds!";
}
}
if (type == "Checking") {
myCheck = myAccts[x]->GetDerived();
double y = myCheck->withdraw(amt);
if (y < 0){
cout << "Insufficient funds!";
}
if (y < 497.5) {
cout << "Withdrawal fee: $ 2.50" << endl;
}
}
}
Checking Account is a child of Savings Account. Sorry.
Your concept is correct and shouldn't require any casting. The whole point of virtual functions is that if you're holding a base class pointer or reference and call a virtual function, the most derived version of this function will be called at runtime.
The error I see is this:
if (type.compare("Checking") == 0) {
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
You are creating a checking account on the stack, then taking it's address and pushing that address into the vector. At the end of the if block myCheck1 will go out of scope and be destroyed. Your vector will have an address to a location in the stack and you will have Undefined Behavior.
Instead do:
if (type.compare("Checking") == 0) {
myAccts.push_back(new CheckingAccount(name, type));
And similar for the other types. Get rid of all of those casts. In this version you will have to delete all of the items in the vector at the end. If you use a std::vector<std::unique_ptr<BankAccount>> then the unique_ptr will take care of cleaning up your allocated objects.
You need to use new to create your accounts... you have:
if (...)
{
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
}
...myCheck1 gets destroyed when leaving that if scope, leaving myAccts with a pointer to an effectively random location on the stack that has undefined behaviour if accessed. Change to:
if (type == "Checking")
myAccts.push_back(new CheckingAccount(name, type));
You will then need to have matching deletes for the vector elements. Googling "C++ new delete tutorial" would be a good idea. The next stage is to learn how to use smart pointers, for example - std::shared_pointer - which remove the burden of remembering to delete.
"Case 3" can be corrected/simplified to:
for (int x = 0; x < myAccts.size(); x++)
if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
double y = myAccts[x]->withdraw(amt);
if (y < 0)
cout << "Insufficient funds!";
if (type == "Checking" && y < 497.5)
cout << "Withdrawal fee: $ 2.50" << endl;
}
Notice in particular the double y = myAccts[x]->withdraw(amt); - the virtual function makes sure the right version is called without you having to do anything type-specific in the calling code.
Have you tried something like this?
class base
{
public:
inline DerivedClass *GetDerived() {return (DerivedClass*)this;}
...
};
DerivedClass *myDerived = myBase[i]->GetDerived();

Passing struct between functions C ++

I've searched but haven't been able to get what I want...
I'm doing a little game. And I got this struct that contains the player details.
struct Player
{
string name;
int level;
int exp;
int hp; // life
int mp; // mana
int shield;
};
And when in the menu, the user chooses to start a new game, it goes to this function:
int StartNewPlayer(string name)
{
Player player;
player.name = name;
player.level = 1;
player.exp = 0;
player.hp = 20;
player.mp = 5;
player.shield = 0;
*pass/return the struct here*
}
Then I have a function that prints the game board, and where I should use the data from the new player struct, for example:
void game_board ()
{
cout << "Hello!" << player.name;
(...)
}
Finally, somewhere in main I have:
int main ()
{
StartNewPlayer(new_game());
game_board();
}
that calls all the functions above.
But I can't figure it out... I tried references, pointers without luck.. I need some help here please...
How about this?
Player StartNewPlayer(string name)
{
Player player;
player.name = name;
player.level = 1;
player.exp = 0;
player.hp = 20;
player.mp = 5;
player.shield = 0;
return player;
}
void game_board(Player player)
{
cout << "Hello!" << player.name;
(...)
}
int main ()
{
Player player = StartNewPlayer(new_game());
game_board(player);
}
Do not create extra copies of the data with complex datatypes by using pass-by-value
Use pointers instead to pass the address of the variable that can be modified in the function. The changes will be reflected in the caller's function as well.
void StartNewPlayer(string name, Player *player)
{
player->name = name;
player->level = 1;
player->exp = 0;
player->hp = 20;
player->mp = 5;
player->shield = 0;
}
void game_board(Player* player)
{
cout << "Hello!" << player->name;
(...)
}
int main ()
{
Player player;
StartNewPlayer(new_game(), &player);
game_board(&player);
}
Alternative using pass-by-reference:
If you're a fan of references, (which is just a clever compiler-trick that makes use of pointers internally again):
void StartNewPlayer(string name, Player& player)
{
player.name = name;
player.level = 1;
player.exp = 0;
player.hp = 20;
player.mp = 5;
player.shield = 0;
}
void game_board(Player& player)
{
cout << "Hello!" << player.name;
(...)
}
int main ()
{
Player player;
StartNewPlayer(new_game(), player);
game_board(player);
}
I would suggest returning a pointer to a Player struct. If you return a "reference" like you are doing right now, it will call the copy constructor of Player which can lead to further complications.
Normally, at the end of StartNewPlayer(...), the Player you declared there will cease to exist as the object scope will end, so when you return it, the c++ compiler gets that you want to keep the object alive and will create a copy for you, invisibly. If you return a pointer to it, you really are returning the object you allocated in your function.
Suppose that you have pointers in your Player structure, such as
struct Player
{
int level;
char* name; //lets assume you did it like that
}
When you are returning the Player, the int will be copied, but the char* will not. ints are easy to handle while char* need all kind of tricky functions like strlen and strncpy. The more complex your Player struct becomes, the more problem you will face by using the default copy constructor.
Another solution would be to declare a copy constructor yourself for the Player struct ( really, you could use classes since they are mostly interchangeable in c++ ).
Player(const Player& p)
{
name = p.name;
level = p.level;
// and so forth
}
So I would use
Player* StartNewPlayer(std::string name)
{
Player* player = new Player();
player->name = name;
player->level = 1;
// snip
return player;
}
At the end of your program, be sure to delete player otherwise you will have a memory leak