Properly Store Object in Vector - c++

I'm brand new to C++, and this is my first time posting (recipe for disaster). I've spent about a day trying to resolve my problem/question, in the mean time finding no easily recognizable solution on the forums. It's possible that my question has already been asked or that a solution has already been posted but I've overlooked it or misunderstood it. A similar post exists here, but it's not obvious to me what to do with the information.
I'll provide a concise, high-level summary of the point of this code. I'll then ask the specific question(s) to which I'm unable to find an answer, and I'll follow with the code I've written.
SUMMARY: I'm creating a program to help do bookkeeping for a game. The game may have any number of players, and each player having a small list of attributes/members (e.g. playerName, playerAllegiance, etc.) which are elements of a Player class/object. The user is first asked to input the name of each player (enteredName), and the program must create a new Player object for each name that's entered. This seems to be appropriately handled by a dynamic array, so I chose to use a vector (called playerIndex) to store each Player object. A for-loop allows the user to input names, each name instantiating a new Player object which is to be stored (copied?) into playerIndex using vector::push_back. At the conclusion of the for-loop, the user should be left with a vector of Player objects, each Player object storing a name in its playerName member.
QUESTION/PROBLEM: The code seems to work proprly when monitoring the vector inside the aforementioned for-loop. Immediately after the user enters the Nth player's name, the program spits out the playerName string stored in the Nth element of playerIndex using a Player class function getPlayerName() [actual code: playerIndex[playerCounter].getPlayerName()]. As soon as the user enters a blank playerName (i.e. presses enter without entering a name), it indicates the user has entered all player names, so the for-loop terminates. Just after this loop, a loop which is designed to output the playerName of each Player object stored in playerIndex does not output the expected names. I don't know why this is happening, but based on my very minimal knowledge of constructors, I'm guessing it has something to do with a copy or move constructor for the Player class. Can anyone clearly explain how to handle this issue? I fear I may be making a pitifully stupid, novice mistake and/or misunderstanding a key concept of C++.
CODE: This code is cropped/simplified to be as clear as possible. For example, the Player class is shown to have only one member (playerName) while, in the original code, it has four or five other members.
//HeaderPlayerClass.hpp
#include <iostream>
#include <string>
#ifndef PLAYERCLASS_HPP
#define PLAYERCLASS_HPP
using std::string;
class Player {
private:
string *playerName;
public:
Player();
Player(string);
~Player();
string getPlayerName();
};
#endif
//PlayerClass.cpp
#include "HeaderPlayerClass.hpp"
#include <iostream>
#include <string>
using std::string;
Player::Player() {
playerName = new string;
}
Player::Player(string enteredName) {
playerName = new string;
*playerName = enteredName;
}
Player::~Player() {
delete playerName;
}
string Player::getPlayerName() {
return *playerName;
}
//main.cpp
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include "HeaderPlayerClass.hpp"
using std::cin;
using std::cout;
using std::string;
using std::vector;
int main(int argc, char** argv) {
string buffer;
vector<Player> playerIndex;
int playerCounter = 0;
for(;;) {
if(playerCounter==0) {
cout << "\nEnter player name and press enter; leave blank and press enter to continue.\n";
}
cout << "\nPlayer " << playerCounter+1 << ":";
getline(cin, buffer);
if(buffer == "esc") {
cout << "PROGRAM EXITED BY USER\n";
return 0;
}
if(buffer.empty()) {
break;
}
playerIndex.push_back(Player(buffer));
cout << "Player " << playerCounter+1 << "'s name:" << playerIndex[playerCounter].getPlayerName() << "\n";
++playerCounter;
}
for(int ii = 0 ; ii < playerIndex.size() ; ii++) {
cout << "\nThis should display player " << ii+1 << "'s name:" << playerIndex[ii].getPlayerName();
}
return 0;
}

class Player {
private:
string *playerName;
Do not store a pointer to the string, store the string itself
class Player {
private:
string playerName;
Constructor
Player::Player() {
playerName = new string;
}
This is unnecessary if you store the string itself - the default constructor will initialize it for you.
Here is where the problems start - this will leave you vulnerable to memory leaks unless you carefully write a destructor
Player::Player(string enteredName) {
playerName = new string;
*playerName = enteredName;
}
All you need, if you store the string itself:
Player::Player( const string& enteredName)
: playerName ( enteredName )
{}
You constructor is really scary. Just leave things to the default destructor when you store the string itself
Player::~Player() {
delete playerName;
}
Next, you are working too hard keeping your own counter. std::vector maintains its own count and using
playerIndex.size()
will save trouble and bugs

Related

C++: Program crashes when I try to access a private class string variable. Why is this, and what can I do to fix it?

I am writing a bank account program that provides a menu system for the user, which allows them to choose between 4 options: A) Add a customer, B) Print all customer data, C) Update customer data, and D) Exit program. Each option performs separate tasks. The option I am focused on for this question is Option A.
Option A is supposed to generate a new bank account object and ask the user to input the account holder name, the initial deposit for the account (how much money to start with for the new account), and the interest rate. It then needs to set these values to the correct private values in the bankAccount class for any given object.
Code for main.cpp before Option B (there is a little more code after this, hence the lack of backward brackets at the end, but I want to try and keep this more concise):
#include <iostream>
#include <string>
#include <cstdlib>
#include "header.h"
#include "implementation.cpp"
using namespace std;
int main()
{
//array of bankAccount class objects (up to 20)
bankAccount account[20];
string menuInput = ""; //used for menu loop input
string accNameInput = ""; //used for account customer name user input
float depositInput = 0; //used for initial deposit input
float interestInput = 0; //used for initial interest input
// int customerCount = 0; //used to compare to static int to determine # of customers in memory
int static i = 0;
//while loop keeps user in the menu until they choose to exit program
while (true)
{
cout << endl << "Enter a letter option below: "
<< endl << endl << "A: Add a customer" << endl << "B: Print all customer data available"
<< endl << "C: Update customer data" << endl << "D: End program" << endl << endl;
cin >> menuInput;
//Option A: Add a customer
if (menuInput == "A" || menuInput == "a")
{
//checking for max customer limit
if (i > 19)
{
cout << endl << "Cannot add customer; Max customer capacity reached." << endl;
}
else //
{
///Creates a new customer account and asks for new customer name,
///initial deposit amount, & interest
cout << endl << "Bank account #" << (i + 1) << " created." << endl;
bankAccount account[i]; //new bank account object created
//time to set the name for our new customer...
cout << endl << "Enter customer name for account #" << (i + 1) << ": " << endl;
cin >> accNameInput;
//setting initial deposit amount
cout << endl << "Enter initial deposit amount for account #" << (i + 1) << ": " << endl;
cin >> depositInput;
//setting initial interest rate
cout << endl << "Enter interest rate (without % sign): " << endl;
cin >> interestInput;
account[i].setInterestRate(interestInput);
account[i].setBalance(depositInput);
account[i].setAccountHolderName(accNameInput);
//increments the account number counter
i++;
}
}
The problem persists with setAccountHolderName() found on the last line here:
account[i].setInterestRate(interestInput);
account[i].setBalance(depositInput);
account[i].setAccountHolderName(accNameInput);
When I call the class functions setInterestRate and setBalance to set the input values to their respective private class variables, the program proceeds like normal and takes the user back to the main menu as it should. But calling setAccountHolderName crashes the program and returns this value: -1073741819 (0xC0000005).
I'll include some code from the header and implementation files below to show how I have accountHolderName code programmed in:
header.h (includes accountHolderName get/set functions):
///Name of file: header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class bankAccount
{
//private data values
string accountHolderName;
int accountNumber;
//static int accCounter; //keeps track of account numbers
float balance;
float interestRate;
public:
//Default constructor
bankAccount();
///Getters/setters for all private member variables
//accountNumber
int getAccountNumber();
void setAccountNumber(int accNum);
//accountHolderName
string getAccountHolderName();
void setAccountHolderName(string accName);
implementation.cpp (includes accountHolderName implementation):
///Name of file: implementation.cpp
#include <iostream>
#include <string>
#include "header.h"
using namespace std;
//static int definition (guess you have to define static members here?)
//int bankAccount::accCounter;
//Default constructor
bankAccount::bankAccount()
{
accountHolderName = "";
accountNumber = 0;
// accCounter = 0;
balance = 0;
interestRate = 0;
}
///Getters/setters for all private member variables
//accountNumber
int bankAccount::getAccountNumber()
{
return accountNumber;
}
void bankAccount::setAccountNumber(int accNum)
{
accountNumber = accNum;
}
//accountHolderName
string bankAccount::getAccountHolderName()
{
return accountHolderName;
}
void bankAccount::setAccountHolderName(string accName)
{
accountHolderName = accName;
}
It seems like messing with the code in certain ways (such as completely deleting the code that comes after Option A's code, commenting out accountHolderName = ""; in the default constructor, etc.) will temporarily allow account[i].setAccountHolderName(accNameInput); to function without crashing the program, but this is incredibly inconsistent and confuses me even more. I'm not sure if the issue has to do with memory or what. I have also tried using cout to see if the input variable accNameInput from main.cpp is getting stored to, which it is.
Sorry if this is simply too much code or explaining; this is my first post and I just wanted to provide a good chunk of my code so you could see the full scale of things. All I am trying to do here is access the bankAccount class private string variable, accountHolderName, and store user input into each new object.
I tried troubleshooting this issue online but couldn't seem to find anything that had a similar issue. I'm not sure what I'm missing; any help or guidance would be incredibly appreciated.
Technically, this line:
bankAccount account[i]; //new bank account object created
Isn't allowed in C++, but some compilers (g++) allow it as an extension since they do formally support variable stack arrays with the C compiler.
Further, that declaration overrides the variable of the same name declared at a higher scope
But then you get to these lines:
account[i].setInterestRate(interestInput);
account[i].setBalance(depositInput);
account[i].setAccountHolderName(accNameInput);
But valid indices of an array range for 0..i-1 So you're already in undefined behavior with your array index out of bounds.
I suspect you really meant this:
bankAccount newAccount; //new bank account object created
...
newAccount.setInterestRate(interestInput);
newAccount.setBalance(depositInput);
newAccount.setAccountHolderName(accNameInput);
account[i] = newAccount;
i++;

Movement system with functions for text RPG

I am a beginner student of C++ and I am currently working on a text RPG. I've been able to implement various functions that help the user check location, interact with items, check inventory, look around, and so on, but I cannot seem to get a good working movement system.
Now, I understand that OOP is obviously going to be MUCH more efficient and less frustrating than going the function route, but I am doing this for a class, and we haven't learned anything about classes/objects yet (We haven't even gone over vectors/arrays in our class).
Here is my code to try and get the movement working:
#include <iostream>
#include <string>
#include <boost/algorithm/string.hpp>
using namespace std;
void movement(string action, int& currentRoom) {
if (action == "MOVE NORTH") {
if (currentRoom == 1) {
currentRoom = 2;
// This part is just to check if the loop happened and changed values.
cout << currentRoom << " " << "You are now in room two." << endl;
}
}
}
int main() {
int currentRoom;
string action;
cout << "Type 'move [direction]'" << endl;
currentRoom = 1;
getline(cin, action);
boost::to_upper(action);
// This is to check (for testing purposes for me) to see if the string
// converted to uppercase properly.
cout << action << endl;
getline(cin, action);
movement(action, currentRoom);
}
Now, this is not final code that I'm implementing into my game. I've just created a small file to try and work out the logic/syntax of this movement function. When I run this code, I am able to type in 'move north' and it successfully translates into MOVE NORTH, but the function doesn't seem to be calling or doing anything. What am I doing wrong here? Is there any way I can make this easier for myself, without fully leaning into OOP?
Like I said, I haven't been able to learn classes/objects aside from a bit of reading I've done online, and I feel like I would be taking on too much right now to try and learn/implement them properly in such a short time... but maybe it would be for the better if I did? I am not sure.
Any help and input is greatly appreciated.
int main() {
int currentRoom;
string action;
cout << "Type 'move [direction]'\n";
currentRoom = 1;
getline(cin, action);
boost::to_upper(action);
cout << action << '\n'; // here you verify
getline(cin, action); // but then you read again from stdin
movement(action, currentRoom); // and pass it on without changing it toupper.
}

Passing array off as reference in c++ without using pointer

I have a quick question regarding an assignment I have to complete for C++. The teacher has required that I include the functions below:
void getPlayerInfo(Player &);
void showInfo(Player);
int getTotalPoints(Player [], int);
But I'm having trouble working on the first function....I'm not sure if I'm calling the array of structures properly. Can someone look it over and see what I'm doing wrong? I fiddled with it a bit and I'm able to call the array and pass off a pointer to the array but the teacher requested the "&" symbol be there so there must be another method I'm unaware of. Please help! Thanks
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
// Structure to hold information about a player
struct Player
{
string name; // to hold the players name
int number; // to hold players number
int points; // to hold the points scored by the player
};
// Function prototypes
void getPlayerInfo(Player &); // function to get the players information from the user
void showInfo(Player); // function to show the table
int main()
{
const int numPlayers = 12; // Constant to hold the number of players
Player team[numPlayers]; // array to hold 12 structures
// Gather information about all 12 players
getPlayerInfo(team);
showInfo(team);
return 0;
}
// Function to get the players info
void getPlayerInfo(Player& team)
{
for (int count = 0; count < 12; count++)
{
cout << "PLAYER #" << (count + 1) << endl;
cout << "----------" << endl;
cout << "Player name: ";
cin.ignore();
getline(cin, team[count].name);
cout << "Player's number: ";
cin >> team[count].number;
cout << "Points scored: ";
cin >> team[count].points;
cout << endl;
}
}
getPlayerInfo() does not accept an array, it accepts a reference to a single Player object.
You need to call getPlayerInfo() for each player in your array. Move your loop outside of getPlayerInfo() and into main().
You've misunderstood the intent of these functions.
Guessing from the information you've provided, getPlayerInfo is intended to get the information of an individual player, and showPlayerInfo is intended to show the information of an individual player.
You're trying to use these functions to do something they aren't intended to do, so it's a good thing that you are having trouble figuring out how to call and how to implement them.
Consider this experience as a object lesson in requirement gathering.

Sorting, adding, removing, and printing objects in C++

I initialized objects in my code using a house number (int) and the person's name (string). This part of my code works and it displays the house number and person correctly.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//AddressBook class definition
class AddressBook
{
public:
//Declare a constructor that has one parameter for each data member
AddressBook(int, string);
//Declare a set method for house number
void setHouseNum(int);
//Declare a get method for house number
int getHouseNum();
//Declare a set method for name
void setName(string);
//Declare a get method for name
string getName();
void displayAddressBook();
private:
//Declare a int data member for house number
int houseNum;
//Declare a string data member for name
string name;
};
// constructor
AddressBook::AddressBook(int num, string personName)
{
setHouseNum(num);
setName(personName);
}
void AddressBook::setHouseNum(int num)
{
houseNum = num;
}
int AddressBook::getHouseNum()
{
return houseNum;
}
void AddressBook::setName(string personName)
{
name = personName;
}
string AddressBook::getName()
{
return name;
}
void AddressBook::displayAddressBook()
{
cout << getHouseNum() << " " << getName() << endl;
}
int main()
{
AddressBook addressBook(3, "Jim");
addressBook.displayAddressBook();
AddressBook addressBook2(5, "Bob");
addressBook2.displayAddressBook();
AddressBook addressBook3(2, "Jeb");
addressBook3.displayAddressBook();
string command;
cout << "Available Commands Are:" << endl;
cout << "Add, Remove, Sort, Print" << endl;
return 0;
}
Output:
3 Jim
5 Bob
2 Jeb
Available Commands Are:
Add, Remove, Sort, Print
The next part is what I'm having trouble with. I'm supposed to read the user input to see what they want to do and perform the operations listed above. So, if they say Add, and then enter "2 Jeb", the code has to say 2 already exists. If it doesn't exist, it has to add it as an object and print the entire list back out.
I know that you can use getline(cin,name) to get the name from the user but I do not know how to implement that in my code to do what I want it to. Once I get the user input string, how do I create an object with it? Also, how do you read an integer the user inputs into an object because I read that getline works for strings and not integers.
Once I read the integer and string input from the user, how do I check if the house number already exists in my objects? If I can get some help with just these two things I can figure the rest out.
Thank you.

Writing an object with a pointer to another object to an ASCII file (C++)

I'm making a program that keeps track of different employees. Some of the employees have partners (wifes and husbands), so all of the Employee objects have a data member called "Partner* partner" (a pointer to a Partner object).
My problem comes when I want to write an Employee to a file. I can successfully write all of the Employee data (name, address, birth date etc.) to the file, but I don't know how to write the partner to file. I have a function in the Partner class called "writeToFile" which outputs all of the partner data, but I don't know how to "connect" it to the correct Employee object. I tried to just output the "partner"-object to the end of the file, but that just added a bunch of zeros.
Should I use two separate files (one for employees and one for partners), or should I just append the partner data to the employee data? Wouldn't that mess up the file structure when reading it back in, since only some of the employees have partners and some of the partner objects just point to NULL?
My classes inherits each other, so both the Partner and Employee class inherits the Adult class, which again inherits the Person class.
Can anyone give me a "pointer" to what is the best way of writing an object which has a pointer to another object inside it? Here's my temporary code btw, if it is of any interest:
#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
#include <cstdlib>
using namespace std;
const int MAXTXT = 80;
class Person {
protected:
char* firstname;
char birthdate[6];
public:
Person() {
char fname[MAXTXT];
cout << "First name: "; cin.getline(fname, MAXTXT);
firstname = new char[strlen(fname + 1)];
strcpy(firstname, fname);
cout << "Birth date (DDMMYY): ";
cin >> birthdate; cin.ignore();
}
void display() {
cout << "\nFirst name: " << firstname;
cout << "\nBorn: " << birthdate;
}
void writeToFile(ofstream & ut) {
ut << firstname << "\n" << birthdate;
}
};
class Adult: public Person {
protected:
char* lastname;
public:
Adult() {
char lname[MAXTXT];
cout << "Last name: "; cin.getline(lname, MAXTXT);
lastname = new char[strlen(lname + 1)];
strcpy(lastname, lname);
}
void writeToFile(ofstream & out) {
out << "\n" << lastname << "\n";
}
void display() {
cout << "\nLast name: " << lastname;
}
};
class Partner: public Adult {
private:
int phone1;
int phone2;
public:
Partner() {
cout << "Phone (mobile): "; cin >> phone1;
cout << "\nPhone (job): "; cin >> phone2; cin.ignore();
}
void writeToFile(ofstream & out) {
Person::writeToFile(out);
Adult::writeToFile(out);
out << "\n" << phone1 << " " << phone2;
}
void display() {
Person::display();
Adult::display();
cout << "\nPhone (mobile): " << phone1;
cout << "\nPhone (job): " << phone2;
}
};
class Employee: public Adult {
private:
int nr;
char* address;
Partner* partner;
public:
Employee() {
}
Employee(int n) {
char adr[MAXTXT];
nr = n;
cout << "Address: "; cin.getline(adr, MAXTXT);
address = new char[strlen(adr + 1)];
strcpy(address, adr);
partner = NULL;
}
void changePartner() {
Partner::Partner();
}
void writeToFile(ofstream & out) {
Person::writeToFile(out);
Adult::writeToFile(out);
out << nr << "\n" << address << endl;
}
void display() {
Person::display();
Adult::display();
cout << "\nAddress: " << address;
if(partner) {
partner->display();
}
}
int returnEmpNr() {
return nr;
}
};
Employee* employees[100];
int lastUsed = 0;
int main() {
}
void writeToFile() {
ofstream outfile("EMPLOYEES.DAT");
ofstream outfile2("PARTNERS.DAT");
outfile << lastUsed << "\n";
for(int i = 1; i <= lastUsed; i++) {
employees[i]->writeToFile(outfile);
}
A pointer is meaningless except to a single run of a program and could very well be meaningless by the time the file is read if the pointer at value has gone out of scope. The odds of the same Partner being in the same spot in memory, assuming space has even been allocated for it, the next time around could be as bad as 1 in 18,446,744,073,709,551,616 and being wrong has often fatal results. And those fatal results mean you got lucky. You could smash perfectly valid memory that belongs to something else, and results in behaviour that is weird, undefined, and much harder to debug than an outright crash.
In C++ pointers are often a sucker bet. Use them as a last resort because they can really ramp up the amount of code you need to write.
I recommend two lists (but not necessarily two files. Both lists can easily exist in one file) one of Partners and one of Employees. The Partners don't seem to need to know about the Employees, so save yourself some trouble and write out and read in the partner list first.
When writing the Employee list, don't store the Partner pointer because it won't work. Instead store the index of the Partner in the Partner list. Then when you read the Employee list, you can read the Partner's position in the Partner table look up that Partner, and point to them. This is why it's much easier to write and read the Partners first; it is hard to look up data in a list that hasn't been read yet.
Or ditch the concept of Partner pointers entirely and always use the index to look the Partner up. This is much safer approach, so long as you always append new partners to the list and never insert into the list or do something silly like shuffle the list.
While we're messing with pointers, take a good solid read of "What is The Rule of Three?" because you are heading into a quagmire of memory management carnage.
As written every time you copy an Employee, you will get another Employee that is copied stupidly. It copies the pointers, not the content, so you now have 2 Employee objects pointing to the same names and Partners. When you free the memory allocated to the partner and names, which you don't and really, really should, the other copy is pointing at memory that your program no longer owns and becomes a ticking time bomb just waiting to crash your program.
Using a std::vector<Partner> (note it is a vector of Partner, not Partner *. This allows the vector to own and manage all of the memory for you and not just the memory needed by the list) and indexes clears up the problem with Partners, because the std::vector manages the memory for you, but the names are still waiting to kill the program. Replacing the char *s with std::string will solve that problem, also by moving the memory management burden from your code to std::string where it was written for you some time in the 1990s and has been under use in millions of programs ever since. Not many bugs left to trip over after 20 years.
If you have to use pointers and arrays of pointers, you need to make custom destructors, copy constructors, move constructors, assignment operators and move operators. That's a lot of extra work.
But those vectors and strings contain pointers and you have to handle writing them carefully. This topic, serialization, has been beaten to death elsewhere, so here's a link to a reputable site that can probably handle it better than I can.