C++ how to format a pointer to an array of classes? - c++

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);
.....

Related

save struct into a binary file and read it

I have array of struct in class,and I want save that in file.
if I put the input ac.mem [i] .username except the username is stored in the file
And if I put the input ac.mem [i] nothing will be saved.
This is part of my code:
const int len=5;
class account {
public:
struct members {
string username;
string password;
int acsess;
}mem[len];
};
class account ac;
....
ac.mem[0] = { "admin","soran",5 };
ac.mem[1] = { "hamid","hamid",4 };
fstream acc1("account", ios::binary);
for (int i = 0; i <= 1; i++) {
acc1.write((char*)&ac.mem[i].username, sizeof(ac.mem[i].username));
}
acc1.close();
....
ifstream acc2("account", ios::binary);
for (int i = 0; i <= len; ++i) {
acc1.read((char*)&ac.mem[i].username, sizeof(ac.mem[i].username));
cout << i + 1 << "." << setw(10) << ac.mem[i].username << setw(20) << ac.mem[i].password << setw(20) << ac.mem[i].acsess << endl;
}
acc2.close();
std::string objects are pretty complex types – they internally maintain pointers to memory. When you just write the internal representation to a file (casting address of to char*) all you write out are these pointers plus possibly some additional management data.
The actual string contents, though, are stored at the locations these pointers point to. When reading back you cannot ever assume to find the same data at the address you've just restored from file (unless the original string object written to still exists – but then playing around with the internals will, if two different std::string objects involved, with 100% probability lead to undefined behaviour due to double deletion, if not reading and writing them from/to memory that way already is).
What you actually want to print to file are the contents of the string – which you get by either std::string::c_str or alternatively std::string::data. You might additionally want to include the terminating null character (hopefully there are no internal ones within the string...) to be able to read back multiple strings, stopping reading each one at exactly the null terminator, then writing to file might look like:
std::string s; // assign some content...
std::ofstream f; // open some path
if(f) // stream opened successfully?
{
f.write(s.c_str(), s.length() + 1);
}
Note that std::string::length returns the length without the terminating null character, so if you want/need to include it, you need to add one to as done above.
Alternatively you can write out the string's length first and then skip writing the null character – with the advantage that on reading back you already know in advance how many characters to read and thus to pre-allocate within your objects (std::string::reserve). For compatibilty reasons over different compilers and especially machines make sure to write out fixed-size data types from <cstdint> header, e.g.:
uint32_t length = s.length();
f.write(reinterpret_cast<char const*>(&length), sizeof(length));
f.write(s.c_str(), s.length());
This approach covers internally existing null characters as well (though if such data exists, std::vector<unsigned char> or preferably std::vector<uint8_t> might be better alternative, std::string is intended for texts).
If you want to use C language, you could refer to the following code.
#include <stdio.h>
#include <stdlib.h>
#pragma warning(disable : 4996)
typedef struct {
char* name;
int phone;
}address;
int main(void)
{
int i;
address a[3];
for (i = 0; i < 3; i++)
{
a[i].name = "jojo";
a[i].phone = "123456";
}
FILE* fp;
fp = fopen("list.txt", "ab");
for (i = 0; i < 3; i++)
{
printf(" % s, % d",a[i].name,a[i].phone);
fwrite(&a[i], sizeof(address), 1, fp);
}
fclose(fp);
return 0;
}

How do you use arrays of objects as parameters? C++

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.

Passing an Array built off of a Struct to a Function that Stores Data into Array from a File

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;
}
}

Returning array from function with data from file

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.

Getting weird number when trying to read a number from file

I'm trying to read a number from a text file, and I'm not allowed to use a binary file.
I've tried two methods to do this, and both return a strange result.
The first method:
char *theNumber;
int i = 0;
while(data>>text)
{
theNumber[i] = text;
i++;
}
returns some weird accented characters.
The second
int theNumber;
while(data>>text)
{
theNumber = text; // I tried theNumber<<text; as well
}
When I cout the result of this one it returns some big number when the text file contained 123.
string filename;
char text;
int p; //first prime number
int q; //second prime number
unsigned long long toBeEncrypted;
cout<<"Enter name of file to encrypt: ";
cin>>filename;
ifstream data;
ofstream encryptedData;
encryptedData.open("RSA_cipher.txt");
cout<<"Please enter two prime numbers:"<<endl;
p = getPrime(1);
q = getPrime(2);
//doing stuff with file
int theNumber;
data >> theNumber;
//int i = 0;
/*while(data>>text)
{
theNumber[i] = text;
i++;
}*/cout<<theNumber;
...//other stuff unrelated to the problem
This code:
char *theNumber;
int i = 0;
while(data>>text)
{
theNumber[i] = text;
i++;
}
Has Undefined Behavior, because you are using theNumber[i] to access an array which you haven't even allocated. You should have done:
char theNumber[255]; // Buffer size depends on the particular application
int i = 0;
while(data>>text)
{
theNumber[i] = text;  
i++;
}
The second attempt:
theNumber = text;
May or may not work, depending on how you defined text. This is impossible to answer without knowing the definition of text.
Anyway, if you want to read in a number from an input stream, just do:
int number;
data >> number;
UPDATE:
In the last code snippet you updated, the data stream is constructed, but never open. It is not associated to any file. Therefore, attempting to read from that stream won't succeed, and nothing will be stored into number (which is uninitialized).
ifstream data;
// data is not associated to any file after construction...
int theNumber;
data >> theNumber;
This does not create storage for your number.
char *theNumber;
It's a pointer. It points somewhere arbitrary, since you haven't assigned an address to it.
Try this.
char theNumber[10]; // Whatever size you need.
Or this.
int theNumber;
You didn't allocate any memory for char *theNumber;.
The theNumber points to a random location and you are printing random characters