Logic Error Assigning and Printing Arrays - c++

void opt3()
{
cout << "ENTER STUDENT NAME>";
cin >> Assigned[Col][Row];//Get New Assignment of Student Name at appropriate Col and Row
Col++;
cout << "ENTER COURSE ID>";
cin >> Assigned[Col][Row];//Get new Assignement of class at appropriate Col and Row
Row++;
return;
}
void opt7(int& Col, int& Row)
{
cin.clear();
cin.ignore();
int x,y,z;
string CouC;
cout << "ENTER COURSE ID>";
getline(cin,CouC,'\n');
bool Ans;
do
{
for (int i=0;i<Row;i++)
{
for (int j=0;j<Col;j++)
{
x = j;
y = i;
Ans = CouC.compare(Assigned[j][i]);
if(Ans)
z=j;
//Compares until it finds the right course ID woop and then sets it to arr$
}
}
}while (Ans == false);
for (y=0;y<10;y++)
{
cout << Assigned[z][y];//Print out the students in the course
}
This is what my problem section of code looks like. I am having difficulty storing the students in the desired class and then printing them out based on the chosen class. I am trying to compare my array Assigned[][] to the appropriate user input and I can`t seem to figure it out. I currently have no compile errors only logic errors. My output when this is run looks weird. It usually prints a random student name with the course name entered.
Help would be appreciated, Thanks!

It looks like the meaning of the two-dimensional array Assigned needs to be defined clearly.
It could be a boolean matrix of students and courses, where the value would indicate if a student is taking a course, or
It could be a list of student and course pairs.
The function opt3() that fills Assigned does something weird: it takes a student and a course, and creates a new column for the student and a new row for the course. So this function seems to use Assigned as a matrix, but it adds both a new student and a new course at the same time. Also the values of this matrix are a mix of student and course IDs, and it has many unused values. This does not make much sense.
If the purpose of the function is to input which student takes which course, it should, depending on the meaning of Assigned, either
search for the student column and course row, and set the value to true, or
add a new student and course pair to the list.

Related

Need to find the average of the grades received by students in their group [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 months ago.
Improve this question
I created a class with students and groups. The group consists of the group name and the number of students. The student class consists of:
student name, price, and logical operation scholarship.
My main goal is to find the average of the grades received by students in their group. However, if a student's scholarship is - > false, then we don't count them. This is the main problem. I created both classes, combined them using a vector, wrote a program so that it would display the average scores of the groups, but when I turn on the program, the console only displays the name of the first group.
I want to clarify that this task is for a two-dimensional array. I just changed it to vectors to learn about the vectors (I don't know vectors well, but I will learn more about vectors in the future).
Here's my code:
#include <iostream>
#include <vector>
using namespace std;
class Student {
public:
string name;
double grade;
bool Grant;
Student() {
string is_Grant;
cin >> name >> grade >> is_Grant;
if (is_Grant == "true") {
Grant = true;
} else
Grant = false;
}
string GetName() {
return name;
}
double Get_Grade() {
return grade /** (Grant)*/;
}
};
class Group {
public:
string G_Name;
int count;
vector<Student> stud;
Group() {
cin >> G_Name >> count;
vector<Student> stud(count);
}
double GetAverage()
{
cout << "\n";
double avg = 0;
for (int i = 0; i < count; i++)
{
avg += stud[i].Get_Grade();
}
return avg / stud.size();
}
};
int main() {
int size;
cin >> size;
vector<Group> grp(size);
for (int i = 0; i < size; i++)
{
cout << grp[i].G_Name << ": Average is " << grp[i].GetAverage() << endl;
}
}
I want to know where I made a mistake. Maybe I forgot to add something?
First, I was using Void GetAvg() instead of a string. At that time, when I only typed in the number of inputs, the program stopped there.
I also tried creating a bool value as a separate function, but it wouldn't output either.
Most importantly, I want to know why the program doesn't find the average value.
So your code is super weird and I'm not sure it can ever work, but there is one obvious error
Group() {
cin >> G_Name >> count;
vector<Student> stud(count);
}
that should be
Group() {
cin >> G_Name >> count;
vector<Student> s(count);
stud = s;
}
Your version creates a local variable called stud which is a vector with the given size, but that has nothing to do with the stud that is a member variable in the Group class. My version uses a different name and then assigns that variable to stud.
This is still weird code, the code I originally wrote in the answer was the more natural
Group() {
cin >> G_Name >> count;
vector<Student> s(count);
stud.resize(count);
}
but this code doesn't work because your strange constructors. This version of the code only creates one Student which is then copied to fill in the vector. So you would end up with count identical students. More on this in the last paragraph.
The reason for your error is that when you call GetAverage you have some value for count but because of the bug the size of stud is still zero, so you are accessing that vector out of bounds and that (probably) is what is crashing your code.
And that brings up another issue with your code. One of the reasons vectors are superior to arrays is that vectors know their own size, and so variables like count are redundant, just query the vector for it's size. Rewriting your code to take advantage of that gives you something like this
class Group {
public:
string G_Name;
vector<Student> stud;
// no count in group
and this
Group() {
int count; // count is now a local variable
cin >> G_Name >> count;
vector<Student> s(count);
stud = s;
}
and this
double GetAverage()
{
cout << "\n";
double avg = 0;
for (int i = 0; i < stud.size(); i++) // get the count from the vector
{
avg += stud[i].Get_Grade();
}
return avg / stud.size();
}
But the elephant in the room is the way you do data input in your constructors. Don't do that. It means that every time you declare a variable the program is going to pause and ask the user for input. It also means that every student you do want to create has to be created with the default constructor, which is why my resize code doesn't work. Think about how restricting that is going to be going forward.

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.

Search through a 2D array and return the entire row

I am new to C++ and I am having a hard time figuring out how 2D arrays work.
I am making a program that takes a .txt file filled with "driver's license records" and reads them in, and then lets the user search through the "database" for for last name, age, or whether the driver is registered to vote (y/n).
A sample .txt file would look like this:
4
Chris Jones 19 Y 374122
Pat Smith 23 N 863901
Kyle Howard 31 Y 673911
Samantha Pratter 27 Y 874309
My main method is simply
int main(int argc, char* argv[])
{
Executive exec(argv[1]);
exec.run();
return (0);
}
Here is the code for my Executive class:
#include <iostream>
#include <fstream>
#include "Executive.h"
#include "DriversLicenseRecord.h"
using namespace std;
Executive::Executive(char* filename){
int n;
ifstream inp(filename);
inp >> n;
num_records=n;
DriversLicenseRecord** record = new DriversLicenseRecord*[n];
for(int i=0; i<n; i++){
record[i] = new DriversLicenseRecord(inp);
}
}
//here is where I am pretty much guessing
void Executive::run(){
int x=0;
do{
cout << "1: Query last name" << endl << "2: Query age range" << endl << "3: Query registered voters" << endl << "4: Quit" << endl;
cin >> x;
if(x==1){
string name;
cout << "Enter last name: ";
cin >> name;
/**for(int i=0; i<num_records; i++){
if(name==record[i]){
cout << record[i];
}
}*/
}
else if(x==2){
int max;
int min;
cout << "Enter age range: " << endl << "min: ";
cin >> min;
cout << "max: ";
cin >> max;
}
else if(x==3){
}
}while(x!=4);
}
And here is my DriversLicenseRecord class:
#include "DriversLicenseRecord.h"
using namespace std;
DriversLicenseRecord::DriversLicenseRecord(ifstream& inp){
inp >> first_name;
inp >> last_name;
inp >> age;
inp >> vote;
inp >> license;
}
Firstly I want to know if I'm reading in the values correctly, it is my understanding that it skips reading in white space, so the DriversLicenseRecord should be getting the correct values.
Secondly I have no idea how to search through this, and return the whole row.
Below is an example of output with a given .txt file:
1. Query last name
2. Query age range
3. Query registered voters
4. Quit
3 // user input
Chris Jones 19 Y 374122
Kyle Howard 31 Y 673911
Samantha Pratter 27 Y 874309
Just a small push in the right direction would be very helpful, I've been struggling with this problem all week and haven't made much progress.
Thank you!
There are a couple errors in your code, but first I'd like to say that there is (1) no need for a 2D array here and (2) you don't create a 2D array in Executive::Executive(). For (1): all you need in this task is an one-dimensional array (or container) of DriversLicenseRecord objects. Then, you can query the fields of individual objects and compare their values to the query to search for specific records. For (2), what you have created is simply an one-dimensional array of pointers to DriversLicenseRecord objects.
Here is where the errors appear.
Firstly, the variable records is local to the constructor. Once the c'tor returns, records will be destroyed. You won't be able to access it outside the constructor. Also, the memory you allocated will be lost, creating a memory leak.
Secondly, while creating the array is correct, iteration is not. Here's how you can iterate over the array and query the fields:
for(int i=0; i < num_records; i++){
// note the -> : because we're using a pointer, not the object itself
if(name == m_records[i]->first_name){
cout << m_records[i]->first_name;
// or, if you define operator<<(istream&, const DriversLicenseRecord&):
cout << *(m_records[i]);
}
Finally, why you had to use a dynamic array. Thing is, you don't know the number of entries until you read the file, and you can't create a variable length array other than with new, except inside a function as a local variable, but then see #1: it's lost on function exit. However, you could create a dynamic array of records, not pointers to records. For that, you need to supply a default constructor to DriversLicenseRecord, and then simply fill in the fields on the fly from the file. (Not the syntax you used with DriversLicenseRecord::DriversLIcenseRecord(istream&), that's not the default c'tor.)
Next, this is how I would go about this problem, using STL containers and algorithms.
1. Switch to std::vector, which has the benefit of being safer and more convenient to use.
2. I honestly disliked the idea of creating the D.L.R. class with an istream parameter. What you can do, if you want to use streams, is to define istream& operator>>(istream&, DriverLicenseRecord&) and then use the beautiful STL syntax like so:
std::istream& operator>>(std::istream& str, DriversLicenseRecord& rec)
{
std::string str;
str >> rec.first_name >> rec.last_name >> rec.age >> temp >> rec.license;
rec.can_vote = (temp == "Y");
return str;
}
Then some STL beauty:
class Executive {
typedef std::istream_iterator<DriversLicenseRecord> in_driver_it;
std::vector<DriversLicenseRecord> records;
public:
Executive(const std::string& file)
{
std::ifstream inp(file);
int n; inp >> n;
std::copy(in_driver_it(inp), in_driver_it(), std::back_inserter(records));
}
};
Same fot the output.
Long story short: here is the complete sample code using the standard library, which is not the shortest but simple on the other hand. Running out of space!
You can avoid using the 2D array.
Approach 1
Use a vector<string> instead. Makes things much simpler to handle.
Read from the text file and store each line as a string in the vector.
Then when you are searching for a particular query string all you need to do is process each string in the vector.
So for reading from the input text file you would do something like this:
ifstream inpFile(myfile.txt);
string line;
vector<string> myInpFile;
while(getline(inpFile,line))
{
myInpFile.push_back(line);
}
I'll leave the implementation of the string search as an exercise for you. Take a look at how to process strings here
Approach 2
Alternatively you can just read off what you need straight off the file into a string and then search the string. You wouldn't need DriversLicenseRecord in memory at all. But that's not what your TA appears to be looking for.

Dynamic string array without using vector

I am working on an assignment where we have to create a "student" object with a "course" member array that is dynamic so the user can enter from one course to as many as they'd like in the array. I've been trying this every which way and just cannot get the array to resize and accept new elements. Here is my code as it stands now:
cout << "Enter student name > ";
cin >> name;
Student firstStudent(name);
cout << endl << "Enter \"done\" when you are complete";
cout << endl << "Enter a new course : ";
cin >> course;
while (course != "done") {
numCourses++;
firstStudent.addElement(numCourses, course);//makes the array bigger
cout << endl << "Enter a new course : ";
cin >> course;
}//end while
member variables and constructor:
class Student
{
private:
string name;//name of student
string *dynArray = new string[1];//array
public:
Student::Student(string name)
{
this->name = name;
this->numC = 1;//initialized to one to start with
}
resizing and adding an element to the array:
void Student::addElement(int numCourses, string &course) {//DYNAMIC ARRAY
if (numCourses > this->numC) {//checks if there are more courses than there is room in the array
this->numC = numCourses; //this changes the member variable amount
dynArray[numCourses] = course;//adds new course to array
string *temp = new string[numC];//create bigger temp array
temp = dynArray;//write original to temp
delete[]dynArray; //this tells it that it's an array
dynArray = temp;//make dynarray point to temp
}
else {
dynArray[numCourses] = course;//adds new course to array
}
}//end addElement
Even if I manage to get rid of compile errors, it will often come up with runtime errors. I'm sure it has to do with pointers/copying arrays but I'm just learning and haven't yet mastered these concepts.
Edit: dynArray[numCourses] = course; creates a char array. ie if course = "one", dynArray[0] = "o", dynArray[ 1] = "n", etc. Does anyone know how to keep this from happening?
screenshot
The first line that that doesn't look quite correct is this one:
dynArray[numCourses] = course;
Before it is checked that numCurses > numC, which means that dynArray[numCourses] accesses memory out of bounds (the bounds are 0 to numC - 1.
The other thing that strikes me as interesting is that the addElement method takes numCourses as an argument. I would expect that it should behave similar to std::vector<T>::push_back. Are you sure this argument is necessary?
The last thing I want to comment on is that the values from dynArray are not copied. Have a look at std::copy_n on how to do the copy.
I'm not sure if this would be considered cheating, but here's a break down of how std::vector is implemented. You could use that as a reference and just pick out the parts you need.
http://codefreakr.com/how-is-c-stl-implemented-internally/

How would I go on as to "return" string arrays between functions?

Hey so I was experimenting what I knew and realized when I tried passing a string value with return it wasn't supported, any idea? Sorry if my code is noob style (I only have 2 months of experience), I was planning on splitting the code between functions but I can't seem to do it because returning my array of strings cant be done with return :( Here's the code:
#include <iostream>
#include <math.h>
#include <sstream>
using namespace std;
int itemlist=0;
int c=0;
int r = 0;
int itemlistdec()
{
cout<<"How many items would you like to input?";
cin>>itemlist;
return itemlist;
}
int main() {
itemlistdec();
string item[4][itemlist];//declares item and that columns equal to itemlist whose content is declared right above
for (int c=0;c<itemlist;c++)
{
for (int r=0;r<3;r++) //DETERMINES WHERE EACH RECORD GOES
{
if (r==0)
{
cout<<"Please enter the name of the item ";
}
if (r==1)
{
cout<<"Please input the buying price\n";
}
if (r==2)
{
cout<<"Please input the selling price\n";
}
cin>>item[r][c];
}
}
int calc[3][itemlist];//declaring calc and itemlist
for (int r = 0;r<itemlist;r++)
{
istringstream(item[1][r])>>calc[0][r]; //TAKES BUYING PRICE INTO INT ARRAY FOR CALCULATION
}
for (int r = 0;r<itemlist;r++)
{
istringstream(item[2][r])>>calc[1][r]; //TAKES SELLING PRICE INTO INT ARRAY FOR CALCULATION
}
for (int fart = 0;fart<itemlist;fart++)
{
calc[2][fart] = calc[1][fart] - calc[0][fart]; //REPEATS CALCULATION FOR PROFIT UNTIL ITEMLIST IS REACHED
}
for (int r = 0;r<itemlist;r++)
{
stringstream ss;
ss<<calc[2][r]; //CONVERTS BOTH PROFIT VALUES INTO STRINGS
item[3][r] = ss.str();
}
cout<<"______________________________________________\n"; //DISPLAYS OUTPUT IN TABLE FORM
cout<<"Item\t\tBuying Price\t\tSelling Price\t\tProfit\n";
for (int c=0;c<itemlist;c++)
{
for (int r=0;r<4;r++)
{
cout<<item[r][c]<<"\t\t";
if (r==1)
{
cout<<"\t";
}
if (r==2)
{
cout<<"\t";
}
if (r==3)
{
cout<<"\n";
}
}
}
return 0;
}
I think you can use vector, it's very powerful. Like that:
std::vector<std::vector<std::string> > mySecondVector;
std::vector<std::string> myFirstVector;
myFirstVector.push_back("MyString");
mySecondVector.push_back(myFirstVector);
mySecondVector[i][j]; // to access
And for add, access to an element watch on http://www.cplusplus.com/reference/vector/vector/
Returning an array is a bit goofy. you have to return it as a pointer, and as soon as you do that you lose all size information. You have this handled by itemlist hanging around as a global variable, but global variables impose their own set of limitations. For one, you can never have more than one of these arrays (unless they are all the same size) because multiple arrays would be storing their length in the same spot.
First you have to flip the orientation of the array to get the constant size on the right-most index. Frankly because of locality (all of the data with respect to one item is in the same memory region, so if you load one part of an item into cache you probably load all of it. This almost always results in a much faster program) it's probably better that way, anyway. Then you've probably noticed you can't just return string[][] or anything that looks like it without the compiler barking at you, so you have to play games defining custom data types that you can return.
typedef string (*arrayPtr)[4];
Now you could try
arrayPtr itemlistdec()
{
cout << "How many items would you like to input?";
cin >> itemlist;
string item[itemlist][4];
//load the array
return item;
}
And itemlist knows how big the array is, but you get a new snag. Item goes out of scope on you and is no longer valid. Even more fun, string item[itemlist][4]; isn't even legal C++ because itemlist isn't a constant value. It works as a convenience in some C++ compilers, but not all.
So how about this? Dynamic allocation of the array will make it outlive itemlistdec, but now you'll have to delete the array manually when you are done with it.
arrayPtr itemlistdec()
{
cout << "How many items would you like to input?";
cin >> itemlist;
arrayPtr item = new string[itemlist][4];
//load the array
return item;
}
Now we've got something that works. We can make it a bit more readable (and less at the same time, because a reader has to track down just what an array is in order to know how to use it) by
typedef string (array)[4];
typedef array (*arrayPtr);
arrayPtr itemlistdec()
{
cout << "How many items would you like to input?";
cin >> itemlist;
arrayPtr item = new array[itemlist];
//load the array
return item;
}
This does not extend to variable length on both dimensions or if you absolutely must have the constant on the inside index. For that case you need this bastich:
string ** itemlistdec()
{
cout << "How many items would you like to input?";
cin >> itemlist;
string** item = new string*[4]; // 4 can be any number, but you also have to
// use it in place of 4 in the for loop
for (size_t index = 0; index < 4; index++)
{
item[index] = new string[itemlist];
}
//load the array
return item;
}
Doable, but you now have picked up a bunch of memory management duties. All of the allocated memory must be released with delete[]. In the string ** case, you have to run through the left-most index and delete[] all of the right-most arrays before deleting the left-most.
You can eliminate this with vector<vector<string>>, but in this case it's ugly. Great trick, but doesn't fit the overall program goal.
vector<vector<string>> itemlistdec()
{
int itemlist;
cout << "How many items would you like to input?";
cin >> itemlist;
vector<vector<string>> item(4, vector<string>(itemlist));
for (int c=0;c<itemlist;c++)
{
cout<<"Please enter the name of the item ";
cin>>item[0][c];
cout<<"Please input the buying price\n";
cin>>item[1][c];
cout<<"Please input the selling price\n";
cin>>item[2][c];
}
return item;
}
Note I'm returning the local vector and counting on move semantics to move the vector on return rather than copying it. Also note that you don't need a global itemlist anymore because the vector knows how big it is. You can even add stuff to it later if you want. And also note I ditched the inner for loop on the input. It's not needed.
This works better than the array options, but I still don't like it. Item is calling out for its own class so we can take advantages of the goodies that object oriented programming brings. We can also the correct data types for all of the member variables. That way we can catch bad input as the user enters it and get rid of an extra integer array used by the calculation.
class Item
{
public:
void read(istream & in)
{
// ignoring possibility of total failure of cin here.
cout<<"Please enter the name of the item ";
in >> name;
cout<<"Please input the buying price\n";
while (!(in >> buyPrice)) // loop until we read an integer from the user.
{
in.clear();
cout<<"Invalid. Please input the buying price\n";
}
cout<<"Please input the selling price\n";
while (!(in >> sellPrice))
{
in.clear();
cout<<"Invalid. Please input the selling price\n";
}
return in;
}
// calc logic method goes here.
// output method goes here.
private:
string name;
int buyPrice; // no screwing around with the calc array. numbers are
//read in as numbers or the input fails
int sellPrice;
};
vector<Item> itemlistdec()
{
int nrItems = 0;
cout << "How many items would you like to input?";
cin >> nrItems;
vector<Item> itemlist(nrItems);
for (Item& item : itemlist)
{
item.read(cin);
}
return itemlist;
}
Note I'm not checking validity of all input, just that valid integers were used. Probably sufficient, but ehhhh.... I have accidentally closed stdin by mistake. Not a fun thing to debug. Anyway, you have worse problems if stdin is going down.