Im trying to pass an arrays of objects to a function so that it can be filled with the contents of a text file.
Update: Removed _menu[] from the function parameters and also from main. The code compiles just fine, but now it crashes right after openMenu asks for the cin to the filename.
code:
class Dish {
public:
int _itemNo;
string _category;
string _description;
double _price;
Dish();
Dish(int itemNo, string category, string description,
double price);
}
class DishDb{
private:
int _nElems;
public:
Dish _menu[20];
void openMenu(ifstream &fromFile);
};
void DishDb::openMenu(ifstream &fromFile){
string fileName;
int itemNo;
double price;
string description;
string category;
int numOfDishes = 0;
cout << "Enter file name: ";
cin >> fileName;
ifstream inFile(fileName);
do{
inFile >> itemNo;
_menu[numOfDishes]._itemNo = itemNo;
getline(inFile, category, ':');
_menu[numOfDishes]._category = category;
getline(inFile, description, ':');
_menu[numOfDishes]._description = description;
inFile >> price;
_menu[numOfDishes]._price = price;
numOfDishes++;
}while(!inFile.eof());
inFile.close();
cout << endl << "Menu was loaded.";
}
int main(){
string filename;
cout << "Enter today's transaction file name: ";
cin >> filename;
DishDb DDb;
ifstream inFile;
Dish _menu[20];
DDb.openMenu(inFile);
DDb.display();
return 0;
}
No errors for some reason
By default, arguments in C++ are passed by value.
By the phrasing of your question it seems like you are trying emulate pass-by-reference which is default in many other languages.
What you want to do in that case is have the function accept either a pointer (Dish* dishArr) or reference (Dish& dishArr) to the array. In either case, you can then use the pointer/reference to access the memory where the object resides from inside the function.
Note you will likely want to also pass in the size of the array so that you don't go out of the bounds of the array.
Pointer Example
void mutateDishArray(Dish* dishPtr, int numDishes) {
for(int i = 0; i < numDishes; ++i) {
dishPtr[i] = Dish(); // dereferencing the pointer using the array syntax
// this is equivalent to writing *(dishPtr+i) = Dish(); using the dereference operator '*'
}
}
int main() {
Dish dishArray[10]; // an array with memory for 10 dishes on the stack
mutateDishArray(dishArray, 10); // pass a pointer to the array (an array reference will decay into a pointer so we don't need the address-of operator '&')
}
That answers your question, but to fit your class layout, you may want to alter your code as follows.
You have a member definition for Dish _menu[20] array in the DishDb class, but you never initialize it with a constructor. Instead, you create a Dish _menu[20] array in main(). That array is outside the scope of the method DishDb::openMenu and not at all related to the Dish _menu[20] array defined in the Dish class.
class DishDb{
private:
int _nElems;
int _menuSize;
Dish* _menu;
public:
DishDb();
void openMenu(ifstream &fromFile);
};
DishDb::DishDb(Dish* _menu, int _menuSize)
: _nElems(0) // this is a 'member initializer-list'
, _menuSize(_menuSize)
, _menu(_menu)
{
}
Now, the DishDb constructor will accept a pointer to the array you had already made in main() and its member methods will have access to it through that pointer.
int main(){
string filename;
cout << "Enter today's transaction file name: ";
cin >> filename;
Dish _menu[20];
DishDb DDb(_menu, 20); // The DishDb is now constructed with a pointer to the _menu array on the stack
ifstream inFile;
DDb.openMenu(inFile);
DDb.display();
}
More on member initializer lists: https://en.cppreference.com/w/cpp/language/initializer_list
Here is a trivial example of using an array as a parameter for a function, for reference:
#include <iostream>
void func(int intArr[], unsigned int arrLength) {
for(unsigned int i = 0; i < arrLength; i++) {
std::cout << intArr[i] << '\n';
}
}
int main(int argc, char* argv[]) {
const unsigned int SIZE = 10;
int myInts[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
func(myInts, SIZE);
return 0;
}
Behind the scenes, arrays are treated as pointers when passed into a function. The parameter int intArr[] could have been int *intArr with no change in effect. Incidentally, I see that you made your array of Dish objects a public variable, but you should really make it private because half the reason we encapsulate data within classes is to make sure that other code (that has no business altering our data) cannot change it directly. Instead, you should add a method that can update the array after performing its own checks and another method that can pass out a pointer to the array.
A couple other things to possibly revisit:
Class definitions must end with a semicolon, but you are missing one at the end of your class Dish definition.
!inFile.eof() is actually a really bad test to use to see if you have reached the end of a file. This is because inFile.eof() will only return true after you have attempted to read past the end of the file, but if you try to read past the end of a file you will get a segfault. Rather, you will want a loop condition that will guarantee that the next read from the file you perform is a valid read. You can accomplish this with a loop similar to:
while(inFile >> itemNo && getline(inFile, category, ':') && getline(inFile, description, ':') && inFile >> price){
_menu[numOfDishes]._itemNo = itemNo;
_menu[numOfDishes]._category = category;
_menu[numOfDishes]._description = description;
_menu[numOfDishes]._price = price;
numOfDishes++;
}
I would highly recommend reading this post on Stack Overflow for a better explanation about why this works better.
It looks like you're probably using using namespace std; somewhere outside of what you showed us, but this is considered bad practice. You won't always run into problems when using it, but as you go on to make larger projects the danger level goes up pretty quickly. This other post on Stack Overflow gives a good explanation of why.
Related
I want to read data from a file into different types of variables and add them to a vector. The file has multiple lines with the same type of content separated by spaces (char, int, double, double, double, char). I created a class Atom:
class Atom {
private:
char Atom_Name;
int Fixed;
double X_cord;
double Y_cord;
double Z_cord;
char Represent;
};
And my main function looks like that:
ifstream inFS;
string line;
string FileName = "File_Name.txt";
inFS.open(FileName);
// Verify file opened correctly.
// Output error message and return 1 if file stream did not open correctly.
if (!inFS.is_open()) {
cout << "Error opening " << FileName << endl;
exit(1);
}
vector<Atom> Inputs;
while (getline(inFS, line)){
istringstream ss(line);
char name;
int fixed;
double x_c, y_c, z_c;
char repr;
ss >> name >> fixed >> x_c >> y_c >> z_c >> repr;
for (int i = 0; i<704; i++){
Inputs.push_back(name, fixed, x_c, y_c, z_c, repr);
}
}
The error "main.cpp:38:12: error: no matching member function for call to 'push_back'
Inputs.push_back(name, fixed, x_c, y_c, z_c, repr);
Am I have to somehow override push_back function for multiple variables to be pushed?
The problem is that your vector expects an Atom object not a list of parameters, create an object of atom first and intialize its parameters, so to start off create a constructor in your header file, so that you have something that looks like the following:
Atom::Atom(char name, int fixed, double x, double y, double z){
this->name = name;
this->fixed = fixed;
this->x = x;
this->y = y;
};
After this in your main file create an object of Atom and push it into your vector by calling the constructor.
Use a struct if you just want to store variables in a group. Also, you need to pass an instance of Atom not its members to the vector.
Another problem is that the class you've defined has private members which is inaccessible anywhere in your code except the class itself. It has not a single member function to obtain their values, that's the reason of using struct here, its variables are visible anywhere, just need to be followed by a . (e.g. atom.name access name character array).
Do something like:
#include <vector>
struct Atom {
char name[50]; // define the char array length too
...
};
int main(void) {
Atom atom; // to push_back()
std::vector<Atom> vecAtom;
.
std::cin >> atom.name >> ...;
.
vecAtom.push_back(atom); // passing a struct of `Atom` here
.
.
return 0;
}
In a structs lab assignment that I am doing, the question asks to read statistics about 10 different dinosaurs from a text file and store that information into a struct. I get no errors in the code, however the console is just totally blank.I think i am definitely referencing the array wrong and I have no idea how to fix this.
My code is as follows:
using namespace std;
const int LIST_SIZE = 10;
struct dinosaurInfo {
string dinosaurName;
string dinosaurClass;
string dinosaurHabitat;
double dinosaurSize;
double dinosaurWeight;
int battleRating;
};
void loadData(ifstream& getData, dinosaurInfo *data);
int main()
{
dinosaurInfo data[LIST_SIZE];
ifstream getData;
ofstream giveData;
getData.open("dinosaurRecords.txt");
if (!getData)
{
cout << "Error loading in data." << endl;
}
loadData(getData, data);
getData.close();
system("pause");
return 0;
}
void loadData(ifstream& getData, dinosaurInfo *data)
{
while (!getData.eof())
{
for (int i = 0; i < 10; i++)
{
getline(getData, data[i].dinosaurName);
getline(getData, data[i].dinosaurClass);
getline(getData, data[i].dinosaurHabitat);
cin.ignore();
getData >> data[i].dinosaurSize;
getData >> data[i].dinosaurWeight;
getData >> data[i].battleRating;
}
}
The text file is formatted as follows: (dinosaurname class habitat height weight battle rating).
screenshot of the file below
May someone please help me fix this?
The root problem is use of getline to read the string elements.
getline(getData, data[i].dinosaurName);
getline(getData, data[i].dinosaurClass);
getline(getData, data[i].dinosaurHabitat);
Since these are tokens separated by space, use the >> operator instead to read them.
getData >> data[i].dinosaurName;
getData >> data[i].dinosaurClass;
getData >> data[i].dinosaurHabitat;
Also, avoid use of
while (!getData.eof()) { ... }
See Why is iostream::eof inside a loop condition considered wrong? for details.
A cleaned up version of you function would be:
void loadData(ifstream& getData, dinosaurInfo *data)
{
// Stop when 10 records are read.
// Also, stop reading when getData is an error state.
for (int i = 0; i < 10 && getData; i++)
{
getData >> data[i].dinosaurName;
getData >> data[i].dinosaurClass;
getData >> data[i].dinosaurHabitat;
getData >> data[i].dinosaurSize;
getData >> data[i].dinosaurWeight;
getData >> data[i].battleRating;
}
}
I want to read information from a file and put that information in an pointer to an array of classes. it keeps throwing an exception at us->setUserName(name); but if I change it to us[i]->setUserName(name); it says "expression must have pointer type". How can I fix this?
const int SIZE = 100;
User *us = new User[SIZE];
input.open("Account.txt", ios::in);
if (input)
{
input >> size;
for (int i = 0; i < size; i++)
{
us = new User[i];
getline(input, uName);
us->setUserName(uName);
getline(input, password);
us->setPassword(password);
getline(input, name);
us->setName(name);
}
else
cout << "Error opening file" << endl;
here is the user class:
class User
{
public:
User();
~User();
void setName(string);
string getName();
void setUserName(string);
string getUserName();
void setPassword(string);
string getPassword();
void setFollower(vector<User*>*);
vector<User*>* getFollower();
void setFollowing(vector<User*>*);
vector<User*>* getFollowing();
protected:
string name;
string userName;
string password;
vector <User*>* followers;
vector <User*>* following;
};
us = new User[i];
Here you throw away all the arrays you have created by this moment and allocate a new array and thus eventually get N(O²) leaked space.
First, remove this line. Next, you might use us[i] in the succeeding property settings but since it will be a reference to an array element, not a pointer, you'll need to call its methods via ., not ->.
BTW, in the listing you've provided one brace is missing, the one before else.
As a side note, you are using too many plain pointers in your class. You did manage to handle and cleanup them properly, didn't you?
As another side note, it is generally a bad idea to store usernames+passwords as plain text (are passwords hashes here?). :)
us = new User[i];
Your problem here. This code will create an array has i element, every element is a new object User, and then pointer us will point to this.
I suggest you use vector, it's dynamic array, it's better than a static array with SIZE = 100 in case you are not sure about input file (Account.txt could be contain more than 100 user information)
std::vector<User> v; // create a vector of User
input.open("Account.txt", ios::in);
if (input)
{
input >> size;
User user; // a temporary user
for (int i = 0; i < size; i++)
{
getline(input, uName);
user->setUserName(uName);
getline(input, password);
user->setPassword(password);
getline(input, name);
user->setName(name);
v.push_back(user); // push to vector
}
}
else
cout << "Error opening file" << endl;
if you want to continue to use an array do this
if (input)
{
input >> size;
for (int i = 0; i < size; i++)
{
getline(input, uName);
us[i].setUserName(uName);
.....
I am getting a segmentation fault: core dumped error when I am reading in players file..
I am trying to add both "firstname lastname" to the player struct. I am trying to access the "0th" people and increment their name because i need both first and last, i cant simply fin >> people[i].name in a simply for loop as i do for the card value (not shown) "heart two 2" for example
// deck of cards
// below are initializations
#include <iostream>
#include <fstream>
#include <ctime>
#include <stdlib.h>
#include <string>
using namespace std;
//globals
const int maxCards = 52;
//Structs
struct card {
char suit[8];
char rank[6];
int cvalue;
char location;
};
struct player {
char name[];
int total;
card hand[];
};
//program
int main()
{
char tempfName[100];
char templName[100];
//create struct array(s)
card deck[52];
card shuffledDeck[52];
player people[4];
//set defalt values
for(int i=0;i<4;i++)
{
strcopy(people[i].name,"first last");
}
//open player names file
ifstream fin2;
string fin2Name;
//get file name from user
cout << "Enter player file name...(Players.txt)" << endl;
getline(cin,fin2Name);
fin2.open(fin2Name.c_str());
//check if Players.txt opens correctly
if(!fin2.good())
{
cout << "Error with player file!" << endl;
return 0;
}
else
{
int j =0;
//fin2 >> people[j].name; //prime file
while(fin2.good())
{
//find the length
int index =0, length=0;
while(tempfName[length] != '\0')
{
length++;
}
//now add space after first name
tempfName[length] = ' ';
length++;
while(templName[index] != '\0')
{
tempfName[length] = templName[index];
length++;
index++;
}
tempfName[length]='\0';
int counter =0;
while(templName[counter] != '\0')
{
people[0].name[counter] = templName[counter]; //testing program on "0th" people
counter++;
}
}
}
}
In your struct, name[] and hand[] are of undetermined size. It's hence difficult to read anything into them.
Then, once you've opened the stream, you're trying to determine the length of the unitianalized tempfName[]. This ain't no good: you're not sure it's null terminated and you'll go out of bounds ! This is the origin of your segfault.
Consider initalizing these by declaring them as:
char tempfName[100]{};
char templName[100]{};
Once this is fixed, your code still loops forever on while (fin2.good()) without reading anything, and bravely adding one whitespace to tempfName until you're out of bound.
Now suppose you'd fix all this, set a length to your name and undcomment your stream reading fin2 >> people[j].name; you'd still have a very risky situation: if the data would be longer that what you've foresseen, it would be truncated and the name wouldn't have a terminating '\0'.
Recommendation 1:
Consider using std::string instead of char[] whenever you consider storing a string. Example:
struct player {
string name = "first last" ; // initialisation value: no strcpy() needed !!
int total;
card hand[5]; // length ?
};
Recommendation 2:
Loop using your stream reading as loop condition:
while (fin2 >> people[j].name) { ///!!!
...
j++; // don't foget to increment your counter
}
However be carefull, because the >> will read one string at a time, the string ending at first whilespace (so only firstname).
If you adopt recommendation 1, it would be easy to write:
while (fin2 >> tempfName >> templName) { ///!!!
people[j++].name = tempfName + " " + templName;
}
which should perform pretty muchthe same thing that your loop, but with far less instructions and risks.
Recommendation 3:
If your number of players is fixed, define the max constant and use a for instead of a while to read your data:
const int max_player = 4;
player people[max_player];
...
for (j=0; j<max_player && (fin2 >> people[j].name); j++) // instead of the former while
If your limit of 4 was arbirary, consider using vectors. But that's another story for the moment.
Your struct player definition is not valid:
struct player {
char name[];
int total;
card hand[];
};
The C string fields name and hand need to have a length, e.g.
struct player {
char name[32];
int total;
card hand[32];
};
Your compiler should be giving you an error for this ("incomplete type").
Note also that since you are writing C++ code then it would be better to use std::string rather than C-style char * strings - it will be easier, more robust, and you won't be mixing C and C++ idioms.
I got a C++ program with which I insert the information in a file. I got a second one ( this one ) program to get the data. My goal is to get all the data and return it as array with type Student. In the GetFromFile method I'm getting the information and I can print it, but how I can return it and use it like this in the main function:
int size;
Student *students = getFromFile( "D:\\test.txt", size );
cout << students[0].name;
The error I'm getting is
[Warning] address of local variable `students' returned
This is my code:
struct Student
{
string name;
char egn[11];
short grade;
double avg_grades;
int excused, unexcused;
};
Student* getFromFile(string filename, int &length)
{
fstream file;
file.open(filename.c_str(), ios::in);
file >> length;
Student students[length];
for ( int i = 0; i < length; i++ )
{
file >> students[i].name >> students[i].egn >> students[i].grade >> students[i].avg_grades >> students[i].excused >> students[i].unexcused;
}
file.close();
return students;
}
int main()
{
int size;
Student *students = getFromFile( "D:\\test.txt", size );
cout << students[0].name;
Firstly, this:
file >> length;
Student students[length];
Is non-standard extension. Length of array should be known at compile-time.
Secondly, you're returning pointer to the memory that will be released once array goes out of scope. You should use std::vector instead:
#include <vector>
// ...
std::vector<Student> getFromFile(string filename)
{
// ...
std::vector<Student> students(length);
Also you no longer need to pass length by reference, since std::vector has size member function.
In getFromFile you are returning a pointer to a vector that only exists in that function. Then, in main you have a pointer to some data that is not valid.
You should use Student *students = new Student[length]; in getFromFile, and delete [] students; in main after using the data. But using a vector for this as others say is a better idea.