getline with structure pointer array - c++

I have to learn memory management in class and how to dynamically allocate memory with the new operator.
I have a struct that is
struct Course
{
int courseNumber, creditHours;
string courseName;
char grade;
};
I am trying to fill the member variables with a for loop but I am unsure how to use a getline with courseName. I was able to use a regular cin but it won't work if the class name has spaces.
Below is my code and what I've tried but I get a arguement error that courseArray is undefined.
Course* readCourseArray(int &courses) //Read Courses
{
cout<<"\nHow many courses is the student taking?\n";
cin>>courses;
const int *sizePTR = &courses;
Course *coursePTR = new Course[*sizePTR];
for(int count = 0; count < *sizePTR; count++) //Enter course information
{
cout<<"\nEnter student "<<count+1<<"'s course name\n";
getline(cin,courseArray[count].courseName);
cout<<"\nEnter student "<<count+1<<"'s course number\n";
cin>>coursePTR[count].courseNumber;
cout<<"\nEnter student "<<count+1<<"'s credit hours\n";
cin>>coursePTR[count].creditHours;
cout<<"\nEnter student "<<count+1<<"'s grade\n";
cin>>coursePTR[count].grade;
}
return coursePTR;
}

The pointer to your array is called coursePTR, not courseArray. Just replace the name courseArray with coursePTR.
For this line:
const int *sizePTR = &courses;
You don't have to do that, you can just use courses directly (so, remove all the * from the places you use sizePTR then change sizePTR to courses).
Also I hope you are remembering to delete[] the return value of readCourseArray :)

Related

Increasing class pointer array

In my code, I want to add one student info into my class pointer array and the array size will increase each time a new student is added. Here is my code:
My header file:
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray);}
My Class definition file:
void Student::AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray)
{
for (int i = countStudent; i < sizeOfArray; i++)
{
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent[i]->studentID;
cout << "Please enter student name: ";
cin >> tempStudent[i]->studentName;
}
}
My Main.cpp file
int *totalStudent = new int;
*totalStudent = 1;
int i, j, countStudent = 0;
int sizeOfArray = *totalStudent;
Student *newStudent[*totalStudent];
//Each time a new student is added, I will allocate a new memory for the array element, then add student Info using function.
for (i = countStudent; i < *totalStudent; i++)
{
newStudent[i] = new Student;
newStudent[i]->AddStudent(newStudent, countStudent, sizeOfArray);
countStudent++;
*totalStudent++;
}
When I run my code, I get an undefined reference error, so I do not know If I am able to increase my array or not. I intend to use C++ syntax so I use new and delete. Thank you for your help.
P.S: Here is my new code and it runs great, the only missing is the studentID for the first element in array.
In my main class:
int numStudent = 0;
int i, j, countStudent = 1;
Student *newStudent = new Student[countStudent];
AddStudent(newStudent, countStudent, numStudent);
My Student.h
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
};
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent);
and My Student.cpp
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent)
{
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent.studentID;
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
newStudent[numStudent] = tempStudent;
numStudent++;
if (numStudent == countStudent)
{
Student *newStudentSize = new Student[countStudent + 1];
for (int i = 0; i < numStudent; i++)
{
newStudentSize[i] = newStudent[i];
}
delete []newStudent;
return *newStudentSize;
countStudent += 1;
}
}
Running this code will give me the following result:
StudentID: 11
StudentName: Dat
StudentID: 23
StudentName: Michael
Printing:
StudentID: 0
StudentName: Dat
StudentID: 23
StudentName: Michael
While it doesn't make sense to increase the array for each new student (it's inefficient) here's one way you can do it (I didn't even try to make your code compile because it has a number of issues and is unnecessarily complicated). Note that tempStudent (in the code snippet below) doesn't even have to be created using new. This solution stores Student objects in the students array (although it's easy to modify it to store Student object pointers instead). That said, usually, you'll just want to create an array of large enough size to accomodate all students (just set studentCount to some appropriate number and not 1 like in the example below).
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student(){};
};
int main(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//Student *tempStudent = new Student();
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount, studentCount*2);
studentCount *= 2;
}
}//end while
//print the students info
for(int i=0;i<numStudents;i++){
Student st = students[i];
cout << st.studentID << " " << st.studentName << std::endl;
}
//deallocate the students array or if you place this in the main like you did, the program will exit immediately so there's no need to deallocate
return 0;
}
Student * ReallocateStudents(Student* st, int oldSize, int newSize){
Student * newStudents = new Student[newSize];
//copy the existing values from the old array to the new one
for(int i=0;i<oldSize;i++){
newStudents[i] = st[i];
}
delete [] st; //delete the old array
return newStudents;
}
UPDATE:
Since you don't want to do everthing in the main(), just create a free AddStudents function and do everything there. Alternatively, you can create a
static function inside the Student class. It makes no sense to create AddStudent as a member of Student because that would require you to use an instance of Student to add a new instance, which makes for poor design (not to mention technical issues etc).
int main(){
// some code here
Students * allStudents = AddStudents();
//print students
}//end main
Students * AddStudents(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount,
studentCount*2);
studentCount *= 2;
}
}//end while
return students;
}
This is simple and easy to both understand and maintain so I suggest using this approach.
addStudent does not do anything with the Student object it belongs to. So there is no need to put it in the 'Student' class. (Or you could rather rewrite it so it does something with the student object it belongs to). And it currently does not "add" anything, so the name is confusing.
What is technically wrong with it depends on what you want it to do. Currently it initializes student objects expected to already exist and pointed to by an array, from a specific array index, to the end of the array. That could well be a useful function, if that is what you want it to do. But you then must call it correctly with an array of pointers that point to valid Student objects, which you currently do not.
Currently in main you have a loop that initializes pointers in an array. And each time you initialize a pointer, you call AddStudent(..). The problem is that 'AddStudent()' tries to initialize ALL the student pointed to by your array.
This has two major problems (In addition to all the other problems with your loop).
Each time you create a new student, all your existing students will be
initialized again with new input from std::cin. (So for n students, you will
try to do n*n initializations)
While the loop in main is running, not all pointers in your array points
to existing Student objects. This may result in important data being
overwritten, a program crash or something totally different and unexpected.
You should sit back and reevaluate how you want to do things. Trying to fix single bugs in your existing code, one after another, will just create more bugs.
Just a hint to get you started:
class Student
{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void init_from_cin();
};
And in your class definition file:
void Student::init_from_cin()
{
cout << "Please enter student id (4 digits only): ";
cin >> studentID;
cout << "Please enter student name: ";
cin >> studentName;
}
If you create a new Student like this:
Student *new_student = new Student;
new_student->init_from_cin();
Then after calling init_from_cin(), the Student object pointed to by new_student should be initialized.
How to create and initialize multiple Student objects in a loop, is left as exercise for the reader. But when you do it, you should understand what your lower and upper bounds of your loop are supposed to be. And you should also understand why moving the upper bound further away while your loop is running is a bad idea.
And never forget that sane programming languages start array indexing with 0.

Changing value of element in dynamically allocated array

I have two dynamically allocated array of class objects - student and staff. When a user inputs the age, based on the age I would like to either update the elements of student array or staff array.
But my code below doesn't work. The variable person doesn't get reassigned to staff once assigned to student. All the data I enter goes into student only no matter the age I enter. What is wrong with my code? How can I have one variable and assign it either one or the other array elements based on a condition check?
#include <iostream>
using namespace std;
int main()
{
class info
{
public:
int unique_id;
char* hair_color;
int height;
int weight;
};
class info* student;
student = new info[10];
class info* staff;
staff = new info[10];
for (int i=0; i<10;i++)
{
class info& person = student[i];
int age ;
cout<< "enter age"<<endl;
cin >> age;
if( age > 18 )
{
person = staff[i]; // This assignment doesn't work ??
}
cout<< "enter unique_id"<<endl;
cin >> person.unique_id;
cout<< "enter height"<<endl;
cin >> person.height;
cout<< "enter weight"<<endl;
cin >> person.weight;
}
cout<<" Student "<<student[0].unique_id<<" "<<student[0].height<<"\" "<<student[0].weight<<endl;
cout<<" Staff "<<staff[0].unique_id<<" "<<staff[0].height<<"\" "<<staff[0].weight<<endl;
return 0;
}
You cannot reseat a reference. Once it's set, it's stuck there and any attempts to reassign the reference will be interpreted as a request to assign to the referenced variable. This means
person = staff[i];
is actually copying staff[i]; into person which is an alias (another name) for student[i]. student[i] will continue to receive the inputs read from the user.
The easiest way around this given your current code is to replace the reference with a pointer, which can be reseated.
class info* person = &student[i]; // using pointer
int age ;
cout<< "enter age"<<endl;
cin >> age;
if( age > 18 )
{
person = &staff[i]; // using pointer, but note: nasty bug still here
// You may have empty slots in staff
}
cout<< "enter unique_id"<<endl;
cin >> person->unique_id; // note the change from . to ->
....
But There are ways around this. You can delay creating the reference until you know which array to use. This requires shuffling around a lot of your code and would still leave you with unused elements in the array if you are not careful.
Fortunately there is a much better way to do this using std::vector from the C++ Standard Library's container library.
std::vector<info> student;
std::vector<info> staff;
for (int i=0; i<10;i++)
{
info person; // not a pointer. Not a reference. Just a silly old Automatic
int age ;
cout<< "enter age"<<endl;
cin >> age;
// gather all of the information in our Automatic variable
cout<< "enter unique_id"<<endl;
cin >> person.unique_id;
cout<< "enter height"<<endl;
cin >> person.height;
cout<< "enter weight"<<endl;
cin >> person.weight;
// place the person in the correct vector
if( age > 18 )
{
staff.push_back(person);
}
else
{
student.push_back(person);
}
}

looping with cin inside the loop, thus storing different char*s in an array (c++)

I'm writing a basic program in C++ that creates a student database (name, major and GPA for each student). The user enters how many students will be in the database, and then enters the data items for each student one by one.
I made a loop that executes "x student" number of times and adds the students one by one to an array of Students by calling cin.getline for each data item and then creating an instance of Student with these items which it adds to the array in the next available place.
Name and major are char* variables (they cannot be strings for this assignment).
I'm having (I assume) pointer-based problems, because after putting a watch on the array of students, I realized that as soon as a new student is added to the array, every previous student in the array is set to the same data as the new student. I think it's because the names of the variables are always the same , since theyre in a loop, and I think that when I create a new Student object it keeps being put in the same address, thus changing all previous items in the array.
I have tried deleting the address of newStudent, and I've tried setting it to NULL after it adds a new student, but my program either breaks or the name and major in newStudent become lines of gibberish instead of deleting their content.
Here's the pertinent bits from main.cpp: (I removed the parts where I was trying to delete things and set things to NULL because they were making it worse and I don't know if I was on the right track). this is just the first part of main. it calls other functions later that I wrote, but I tested those and they all work fine. This is the part that's making it all break.
#include<iostream>
#include "student.h"
using namespace std;
int main(){
int numMajors = 0; // keeps track of the total number of Majors
int currAdded = 0; // keeps track of how many students have been added THUS FAR
const int MAX_MAJORS = 100; // max number of majors allowed
const int MAX_LENGTH = 25; // max length of character array
const int TABLE_SIZE = 100; // max table size
int numStudents = 0; // total number of students being added
char* name = new char[MAX_LENGTH]; // cin>>name
char* major = new char[MAX_LENGTH]; // cin>>major
float GPA = 0; // cin>>gpa
//Get Data
cout << "--Student Database Data Entry--" << endl << endl;
cout << "Enter the number of students in registry: ";
cin >> numStudents;
cout << endl << "Enter data for each student: " << endl;
Student table[TABLE_SIZE];
char* majors[MAX_MAJORS];
for(int i = 0; i < numStudents; i++){
cout << " Student " << (i+1) << " : " << endl;
cout << " Last Name: ";
// need to use getline to cin multiple words, so using
// cin.ignore() to eat the trailing \n from cin>>numStudents
cin.ignore();
cin.getline(name, MAX_LENGTH);
cout << " Major: ";
cin.getline(major, MAX_LENGTH);
cout << " GPA: ";
cin >> GPA;
Student* newStudent = new Student(name, major, GPA);
table[i] = *newStudent;
currAdded++;
and here is my constructors and destructor for Student (in student.cpp)
Student::Student() :
name(NULL),
major(NULL),
GPA(0)
{
}
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = name;
this->major = major;
this->GPA = GPA;
}
Student::~Student()
{
if(name)
delete [] name;
if(major)
delete [] major;
}
Student& Student::operator=(Student& student)
{
this->name = student.name;
this->major = student.major;
this->GPA = student.GPA;
return *this;
}
note: when my destructor is called, it also fills the name and major variables it's supposed to be deleting with long strings of gibberish characters, but that's a whole other problem. I commented it out for a while to temporarily get around this problem but I realize that's not a long term solution (the two are probably related)
If there's any more code you'd like to see I would be happy to post it, I'm trying to keep this as short as I can and I'm not sure if more is necessary.
Thank you so much for your insight
You have to copy the strings passed instead of just assigning the pointers passed.
Try this:
#include <cstring> // for using strlen() and strcpy()
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = new char[strlen(name) + 1]; // +1 for terminating null-character
strcpy(this->name, name);
this->major = new char[strlen(major) + 1];
strcpy(this->major, major);
this->GPA = GPA;
}

c++ pointer to a Structure whose member is a pointer

Hi all I am working on an assignment , when given a struct and its members I will have to intialise the members using dynamic memory allocation . This is the code I have used:
# include <iostream>
# include <string>
using namespace std;
Structure
struct Student
{
char * name;
float gpa;
} ;
PROTOTYPES
Student * createStudent(char name[], float gpa);
int main ()
{
int length ;
float gpa ;
char *p;
Student *myPointer ;
Student myStudent ;
myPointer = &myStudent;
cout << " Please enter number of characters you want to enter " << endl;
cin >> length ;
length = length + 1;
p= new char [length +1];
cout << " Please enter name " << endl;
cin >> p ;
cout << " please enter gpa "<< endl;
cin >> gpa ;
myPointer = createStudent (p,gpa);
cout << myPointer->gpa;
cout << (*myPointer).name << endl;
Her's the error when printing the name, but when i see the value of the name before printing it is showing the same as entered by the user:
delete[]p;
p = 0;
system("pause");
return 0;
}
This function creates a student object and assigns name and gpa as passed to the student object and returns a pointer of the student object:
Student * createStudent( char name[], float gpa )
{
Student *studentPtr ;
Student studentObject;
studentPtr = &studentObject;
studentPtr-> name = name;
studentPtr-> gpa = gpa ;
return studentPtr ;
}
Can anyone give me an idea of why the name is not printing. Thank you in advance .
studentObject is allocated on the stack, i.e. it has automatic storage duration, which means it will be destroyed when the function returns. The pointer doesn't keep it alive.
That means myPointer is a dangling pointer; dereferencing it (myPointer->) invokes undefined behavior.
To correct it, simply return the object by value:
Student createStudent(char name[], float gpa)
{
Student studentObject;
studentPtr.name = name;
studentPtr.gpa = gpa;
return studentObject;
}
and
Student myStudent = createStudent(p, gpa);
Edit: I just read that you need to initialize the members using dynamic memory allocation. The above solution doesn't do that. For dynamic memory allocation you have to use new:
Student* createStudent(char name[], float gpa)
{
Student* studentPtr = new Student;
studentPtr->name = name;
studentPtr->gpa = gpa;
return studentPtr;
}
and
myPointer = createStudent(p, gpa);
// use
delete myPointer;
It would probably be a good idea to also allocate a new buffer for studentPtr->name with new, instead of just assigning the pointer; you can see Mateusz's answer about that.
But of course this is just a bad example from your professor. As mentioned by gd1, this kind of code is not good modern C++ and shouldn't end up in real production code.
This is totally wrong:
Student* createStudent( char name[], float gpa )
{
Student *studentPtr ;
Student studentObject;
studentPtr = &studentObject;
studentPtr-> name = name;
studentPtr-> gpa = gpa ;
return studentPtr ;
}
studentObject is a local object, that is destroyed just after the function ends. Returning pointers and/or references to local variables is an error - you should get warning from your compiler.
Another issue is this line:
studentPtr->name = name;
What you do now: allocate a block of memory in main(), pass it to an object, that may use this memory and then free it in main(). You should not do that - objects should be responsible for their content. Are you sure, that this responsibility should be yours? I would suggest you to:
Student* createStudent( char name[], float gpa )
{
size_t name_len = strlen(name);
Student studentObject;
studentObject.name = new char[name_len+1];
strncpy(studentObject.name, name, name_len + 1);
studentObject.gpa = gpa ;
return studentObject;
}
And analogical function to destroy Student:
void destroyStudent(Student* student)
{
delete[] student->name;
student->name = nullptr;
}
Obvious solution would be to use std::string, but I assume, that you have a good reason to do all this manually... don't you?
Oh, and if you really need to return Student as pointer:
Student* createStudent( char name[], float gpa )
{
size_t name_len = strlen(name);
Student* studentPtr = new Student;
studentPtr->name = new char[name_len+1];
strncpy(studentPtr->name, name, name_len + 1);
studentPtr-> gpa = gpa ;
return studentPtr;
}
void destroyStudent(Student* student)
{
delete[] student->name;
student->name = nullptr;
delete[] student;
}

Reading into dynamically created pointer to array of structs

This is a class assignment that must be done using a dynamically created array of Course. I am trying to read into each member variable inside of course inside of my for loop but I'm not really sure how to do it. I did it with my student struct but the difference in this being an array is messing me up because I'm not sure how to proceed with it.
My problem is in the readCourseArray function when trying to read in struct members. If anyone could tell me how I do that I'd be appreciative. I know using the new operator isn't ideal along with many of the pointers being unnecessary but it is just how my instructor requires the assignment to be turned in.
#include <iostream>
#include <string>
using namespace std;
struct Student
{
string firstName, lastName, aNumber;
double GPA;
};
struct Course
{
int courseNumber, creditHours;
string courseName;
char grade;
};
Student* readStudent();
Course* readCourseArray(int);
int main()
{
int courses = 0;
Student *studentPTR = readStudent();
Course *coursePTR = readCourseArray(courses);
delete studentPTR;
delete coursePTR;
system ("pause");
return 0;
}
Student* readStudent()
{ Student* student = new Student;
cout<<"\nEnter students first name\n";
cin>>student->firstName;
cout<<"\nEnter students last name\n";
cin>>student->lastName;
cout<<"\nEnter students A-Number\n";
cin>>student->aNumber;
return student;
}
Course* readCourseArray(int courses)
{
cout<<"\nHow many courses is the student taking?\n";
cin>>courses;
const int *sizePTR = &courses;
Course *coursePTR = new Course[*sizePTR];
for(int count = 0; count < *sizePTR; count++) //Enter course information
{
cout<<"\nEnter student "<<count<<"'s course name\n";
cin>>coursePTR[count]->courseName>>endl;
cout<<"\nEnter student "<<count<<"'s course number\n";
cin>>coursePTR[count]->courseNumber;
cout<<"\nEnter student "<<count<<"'s credit hours\n";
cin>>coursePTR[count]->creditHours;
cout<<"\nEnter student "<<count<<"'s grade\n";
cin>>coursePTR[count]->grade>>endl;
}
return coursePTR;
}
Array subscript operator return an element of the array.
coursePTR[count] is equivalent to incrementing the pointer to the start of array and dereferencing the result, like: *(coursePTR + count). What you get is an object (or a reference to one) of type Course. So you'll need to use 'dot' operator, not 'arrow' operator to access the elements:
cin >> coursePTR[count].creditHours;
You've got another error:
cin >> coursePTR[count].courseName >> endl;
^^^^
This won't compile. endl can only be used on output streams.
Course* readCourseArray(int &courses); // Update the definition to pass "courses" by reference.
Course* readCourseArray(int &courses) // Pass the courses by reference so that your main() has the value updated.
{
cout<<"\nHow many courses is the student taking?\n";
cin>>courses;
/*
You don't need this line.
*/
// const int *sizePTR = &courses;
/*
You've allocated space for "courses" no. of "Course" objects.
Since this is essentially an array of "Course" object, you
just have to use the "." notation to access them.
*/
Course *coursePTR = new Course[courses];
/*
"endl" cannot be used for input stream.
*/
for(int count = 0; count < courses; count++) //Enter course information
{
cout<<"\nEnter student "<<count<<"'s course name\n";
cin>>coursePTR[count].courseName;
cout<<"\nEnter student "<<count<<"'s course number\n";
cin>>coursePTR[count].courseNumber;
cout<<"\nEnter student "<<count<<"'s credit hours\n";
cin>>coursePTR[count].creditHours;
cout<<"\nEnter student "<<count<<"'s grade\n";
cin>>coursePTR[count].grade;
}
return coursePTR;
}