I'm trying to refactor some code to use pointers and am running into a write access violation on my function calls.
I'm making these edits because my homework project requires the usage of the -> member operator as well as constructors and destructors.
One more edit: the input file worked just fine when I was formerly working without pointers, but the moment I added pointers everything broke.
Here's my code:
In main.cpp:
#include "student.h"
int main()
{
/*
TODO:
2. Implement the class such that member pointers can be used to access the members.
3. Implement pointers that point to each of the students' test scores as well as the average test score.
*/
const int numStudents = 15; // Number of students
Student * students = new Student[numStudents]; // Dynamically allocated array of Student objects
Student ** studentsPtr = &students;
// Starting file stream for studentRecords.dat
ifstream student_records("student_records.dat");
// Error checking for file loading
if (!student_records)
{
cerr << "ERROR: The record file could not be opened for reading." << endl;
exit(1);
}
// Load data from file
string current_value;
stringstream newval;
int tempID;
string tempName;
double tempTestScore;
for (int index = 0; index < numStudents; index++)
{
// Store the student ID
getline(student_records, current_value);
newval << current_value;
newval >> tempID;
studentsPtr[index]->setID(tempID);
newval.clear();
// Store the student first name
getline(student_records, current_value);
newval << current_value;
newval >> tempName;
studentsPtr[index]->setFirstName(tempName);
newval.clear();
// Store the student last name
getline(student_records, current_value);
newval << current_value;
newval >> tempName;
studentsPtr[index]->setLastName(tempName);
newval.clear();
// Add each test score.
for (int testScoreIndex = 0; testScoreIndex < numTests; testScoreIndex++)
{
getline(student_records, current_value);
newval << current_value;
newval >> tempTestScore;
studentsPtr[index]->addTestScore(tempTestScore, testScoreIndex);
newval.clear();
}
// Calculate the student's average
students[index].calculateAverage();
}
// Print all data
for (int index = 0; index < numStudents; index++)
{
studentsPtr[index]->printAll();
}
delete[] students; // Free memory pointed to by students array
students = NULL; // Clear the memory.
system("pause");
return 0;
}
In student.h:
#pragma once
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;
const int numTests = 10;
// Student class declaration
class Student
{
private:
// Student ID and name
int id;
string firstName;
string lastName;
// List of student test scores
// vector<double> testScores;
double * testScores = new double[numTests];
// Student average test score
double average;
public:
Student() // Default constructor
{
const int numTests = 10;
id = 0;
firstName = " ";
lastName = " ";
average = 0.0;
}
~Student() // Destructor
{
delete[] testScores;
}
void setID(int); // Set the student ID
void setFirstName(string); // Set the student name
void setLastName(string);
void addTestScore(double, int); // Add a test score to the vector
void calculateAverage(); // Calculate the average of all test scores
void printAll(); // Output all data to the screen for a given student
};
In student.cpp:
#include "student.h"
// setID sets the id value.
void Student::setID(int studentID)
{
id = studentID;
}
// setName sets the name value.
void Student::setFirstName(string studentFirstName)
{
firstName = studentFirstName;
}
void Student::setLastName(string studentLastName)
{
lastName = studentLastName;
}
// addTestScore adds a test score to the vector
void Student::addTestScore(double testScore, int index)
{
testScores[index] = testScore;
// testScores.push_back(testScore);
}
// calculateAverage adds every test score from the vector and divides them by the number of test scores in the list.
void Student::calculateAverage()
{
double totalScores = 0.0;
// for (double index : testScores)
for (int index = 0; index < numTests; index++)
{
totalScores += testScores[index];
}
average = totalScores / numTests;
}
// printAll prints all the data to the screen.
void Student::printAll()
{
cout << "=========================================================\n";
cout << "Student ID:\t" << id << endl;
cout << "Student Name:\t" << firstName << " " << lastName << endl;
cout << setprecision(4) << "Average:\t" << average << endl;
cout << "Test Scores: " << endl;
// Printing the test scores nicely
int scoresPerLine = 5;
for (int i = 0; i < numTests; i++)
{
cout << setprecision(4) << testScores[i] << "\t";
if ((i + 1) % scoresPerLine == 0)
{
cout << endl;
}
}
cout << endl;
cout << "=========================================================\n\n";
}
The error I'm getting is Exception thrown: write access violation. this was 0xCCCCCCCC and it throws the exception at a break point created at
void Student::setFirstName(string studentFirstName) at the line firstName = studentFirstName.
My question is: what exactly is preventing this from working? Am I doing something wrong? I dont' get any errors before I compile everything, so it looks like everything is built ok. I've also tried using a pass-by-reference on that member function, but that's also failing with the same response.
Am I doing something wrong?
Yes, definitely :)
Let's go through it:
Student * students = new Student[numStudents];
... the above allocates a dynamic array of 15 Student objects; so far, so good.
Student ** studentsPtr = &students;
This line is the source of the trouble. You've declared a double-pointer Student ** and initialized it to point to the address of the students pointer. This is legal C++, but note that there is only the standalone students pointer -- in particular, there is not an array of pointers-to-Student in your program anywhere. (There is an array of Student objects but that is not the same thing as an array of pointers-to-Student)
... then a bit later on, comes the actual trouble:
for (int index = 0; index < numStudents; index++)
{
[...]
studentsPtr[index]->setID(tempID); // BOOM!
Here you are trying to use studentsPtr as if it was if it was the base-address for an array of pointers-to-Student, i.e. by offsetting its location by index pointers and dereferencing that location. But it's not really pointing to an array-of-pointers, it is pointing to a single pointer (i.e. it is pointing to the variable students), so whenever index is non-zero, you are invoking undefined behavior and therefore (in your case) you get a crash.
Let's debug it:
Since you didn't provide a complete test case, I changed the number of students to 3 and the number of tests to 0:
student_records.dat
1
Foo
Bar
2
Foo2
Bar2
3
Foo3
Bar3
My crash occurs in setID, but that's okay. this is 0xCCCCCCCC, which is the value given to uninitialized data by MSVC in debug mode. Great, the object pointer is garbage. Where does it come from? We'll go up the call stack to see:
This brings us to the following line in the loop of main that reads the input:
studentsPtr[index]->setID(tempID);
First, let's look at the variables:
We know the object is garbage. We can verify that here. Our object is studentsPtr[index], which is shown with the same uninitialized value. We also see that studentsPtr itself points to the proper first student. Finally, the index variable has the value 1, so we're on the second student.
studentsPtr[1] has a value that MSVC provides for uninitialized memory. Why is it uninitialized? Let's go back to the declaration:
Student *students = new Student[numStudents];
Student **studentsPtr = &students;
studentsPtr is set to be a pointer to a pointer to students. The inner pointer is actually an array of students. The outer pointer, however, is one solitary Student*. When indexing it like studentsPtr[1], we go beyond the single pointer within and trample onward to a nonexistent Student*. Then we try to write to that and the program thankfully blows up early.
The solution is to get rid of the double pointer. All that's needed is a bunch of Students. One pointer is one (unrecommended way to represent an) array:
Student *students = new Student[numStudents];
...
students[index].setID(tempID);
Now since the number of elements is known at compile-time, the recommended type would be std::array (std::array<Student, numStudents> students;), which can be used with the same syntax as the above after its declaration. If the size were not known at compile-time, the recommended type would be std::vector, which also shares the same syntax to access elements.
Technically, you can fulfill the -> requirement using a std::array as well. Simply obtain a pointer to the element and then use the arrow:
(&students[index])->setID(tempID);
More likely, the requirement is still looking for the manual free store memory management that you're doing. It's also easy to fit the arrow into that:
(students + index)->setID(tempID);
If you really, really need the double pointer even though it serves no purpose, remember that your array is the inner pointer, not the outer one:
((*students) + index)->setID(tempID);
If you're thinking the arrow hinders readability in all of these scenarios, you're correct. Perhaps the instructor has something specific in mind where it does not.
What happens when the double pointer is removed?
=========================================================
Student ID: 1
Student Name: Foo Bar
Average: -nan(ind)
Test Scores:
=========================================================
=========================================================
Student ID: 2
Student Name: Foo2 Bar2
Average: -nan(ind)
Test Scores:
=========================================================
=========================================================
Student ID: 3
Student Name: Foo3 Bar3
Average: -nan(ind)
Test Scores:
=========================================================
Success. The average is meaningless because I simplified the input file by changing the number of tests to 0. Long story short, the debugger provides the tools that can get debugging jobs done. Just from the debugger, we reduced the problem to the double pointer pointing to only one thing instead of multiple things. That's a much smaller scope for a problem than the original one.
Related
I am having trouble of understanding how to create an array of pointers to structures. I tried to look up similar examples and threads in the forum but I still cannot get my code to work! As a result, I believe I have written an ugly piece of code that I do not know where it is wrong and how to fix it.
Here is the code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Movie
{
string name;
int numawards;
int nomination;
int year;
};
void *readfile(ifstream &infile, int &n);
int main()
{
ifstream infile;
int n = 0;
infile.open("old_movies.txt");
Movie *oldmovies;
oldmovies = readfile (infile, n);
return 0;
}
//*function documentation//
void *readfile (ifstream &infile, int &n)
{
infile >> n;
Movie *movies;
movies = new Movie[n];
for (int i = 0 ; i < n ; i++)
{
infile >> movies[i]->year >> movies[i]->numawards >> movies[i]->nomination;
infile.ignore();
infile.ignore();
getline(infile, movies[i]->name);
cout << movies[i]->year << " " << movies[i]->numawards << " " << movies[i]->nomination << " " << endl << movies[i]->name<< endl; //the cout here is to test and see if the code works.
}
return movies;
}
The purpose of this code is to read a txt file that contains the movie name, how many awards, how may nominations, and what year it is produced, and then print it out using pointers. Here is what the file looks like:
2
1935 1 3
The Dark Angel
1935 4 6
The Informer
1935 1 8
the first 4 digits represents the year, the second one represents number of awards it has gotten, and the last digit represents the number of times it has been nominated to an award.
Anyway, I am stuck at this part and I am really clueless about what to do here. I just hope that this code is not that bad to a point where there are numerous things to be changed. Any help or advice would be greatly appreciated.
Let's look at what you have here:
Movie *movies;
movies = new Movie[n];
This allocates an array of Movie instances. To allocate an array of pointers dynamically, you need to change this to
Movie** movies;
movies = new Movie*[n];
Now inside the for loop, you need to allocate each Movie instance:
movies[i] = new Movie();
You should also change the readfile() to return a Movie** rather than a void* so that you don't have to use any casts later.
But do you really need an array of pointers? Why not just use an array of structs. This would avoid the extra level of indirection and make your code a little simpler.
this script is supposed to output array values that were inputted by the user into array "store." I am trying to store all the char array values into string temp. I get the error on line 12: "[Error] invalid conversion from 'char*' to 'char' [-fpermissive]." Would appreciate any help!
Edit: so I fixed the declaration and now at least it compiles, but the answer I get on my cmd is all jumbled up. Why is this so? The cmd only correctly couts the first string but after the space, it messes up.
#include <iostream>
#include <cstdlib>
using namespace std;
void coutArray(char[], int);
int main()
{
char store[50];
cout << "enter text: " << endl;
cin >> store;
coutArray(store, 50);
system("pause");
return 0;
}
void coutArray(char store[], int max)
{
string temp = "";
int i = 0;
while (i < max)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
Using input from all answerers I finally got the fixed code:
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
void coutArray(char[], int);
int main()
{
char store[50] = {0};
cout << "enter text: " << endl;
cin.getline(store, 50);
coutArray(store, 50);
system("pause");
return 0;
}
void coutArray(char store[], int max)
{
string temp = "";
int i = 0;
while (i < max && store[i]!=0)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
Thanks everyone. i learned a lot!!!
When you get an input using "cin" your input automatically ends with 0 (NULL).
You just need to add one little piece of code to your while statement.
instead of this :
while (i < max)
use this :
while (i < max && store[i]!=0)
Now it will stop when the input string is finished and won't print any garbage existed in the array beforehand.
To show that cin does add terminating zero, i initialized the array to 46, and put a breakpoint after the cin
so I fixed the declaration and now at least it compiles, but the answer I get on my cmd is all jumbled up. Why is this so?
Not sure what you mean by jumbled up. But since you did not tell us what you typed its hard to know it looks like it worked to me:
> ./a.out
enter text:
Plop
Plop�ȏU�
Notice that since my input is only 4 characters long. This means that a lot of the characters in the array still have undefined (ie random values). This is why I am seeing junk. To get past this initialize the array to have all 0 values.
char store[50] = {0};
Even bettern use a C++ object than handles longer strings.
std::string store;
std::getline(std::cin, store);
Note: passing arrays to functions by value is not a good idea. On the other end they have decayed to pointers and thus do not act like arrays anymore (they act like pointers whose semantics are similar but not identical).
If you must pass an array pass it by reference. But I would use a C++ container and pass that by reference (it is much safer than using C constructs). Have a look at std::string
The declaration of the function is wrong. Should be void coutArray(char *, int);
Look at the Implicit Conversion rules to understand what the compiler can do and what it cannot to do for you.
The issue with your program was that you were probably entering in less characters than the maximum size of the buffer. Then when you passed the maximum size as the parameter to coutArray, you assigned unfilled slots in the char array to temp. These unfilled slots could contain anything, as you have not filled them up to that point.
Your program is still correct, but what would be better would be to use read so that the number of bytes you specify is the minimum number of bytes that can be entered:
std::cin.read(store, 50);
Even better solution would be to use std::string:
std::string store;
std::cin >> store;
// or for the entire line
std::getline(std::cin, store);
It also follows that your coutArray should be changed to:
void coutArray(std::string);
// ...
void coutArray(std::string str)
{
std::cout << str << std::endl;
}
Look at this way
template<typename T, size_t N>
void MyMethod(T (&myArray)[N])
{
//N is number of elements, myArray is the array
std::cout<<"array elements number = "<<N<<endl;
//put your code
string temp;
temp.resize(N+1);//this is for performance not to copy it each time you use += operator
int i = 0;
while (i < max)
{
temp += store[i];
i++;
}
cout << temp << endl;
}
//call it like this
char arr[] = "hello world";
MyMethod(arr);
I am writing this code for a homework assignment. Below is the code that I did and it works the way that it is. I am told that I must declare the constructors. Below the code is one of the header files that we must use. So why is it that the program works if I did not declare the constructor. I have ask the teacher two days ago and she still has not responded back to me.
#include "stdafx.h"
#include <iostream>
using namespace std;
#include "dormroom.h"
#include "textlib.h"
#include <iomanip>
int main( )
{
int numberInRoom1;
int numberInRoom2;
int numberInRoom3;
double totalFunds;
cout << "Enter the number of students in room 1: ";
cin >> numberInRoom1;
cout << "Enter the number of students in room 2: ";
cin >> numberInRoom2;
cout << "Enter the number of students in room 3: ";
cin >> numberInRoom3;
// HERE IS WHERE IM AM TO DECLARE THE CONSTRUCTORS
// THESE ARE THE CONSTRUCTOR FOR EACH OF THE OBJECTS
DormRoom specialA(1, numberInRoom1);
DormRoom specialB(2, numberInRoom2);
DormRoom specialC(3, numberInRoom3);
totalFunds = specialA.getRoomCount() * specialA.getRoomCost() + specialB.getRoomCount() * specialB.getRoomCost() + specialC.getRoomCount() * specialC.getRoomCost();
cout << "The total funds collected for the special rooms are $" << setw(6) << setreal(1,2) << totalFunds << endl;
system("PAUSE");
return 0;
}
this is the header file for dormroom.h
#ifndef DORM_ROOM_CLASS
#define DORM_ROOM_CLASS
// maintain data for a dormitory room
class DormRoom
{
private:
int roomNumber; // room number
int roomCount; // number of students in the room
double roomCost; // cost of the room
public:
// constructor
DormRoom(int roomNo, int number);
// functions return value of attributes
int getRoomNumber();
int getRoomCount();
double getRoomCost();
// change number of room occupants and update cost
void setRoomCount(int number);
};
// ***********************************************************
// DormRoom class implementation
// ***********************************************************
// constructor. initialize room number and number of
// occupants. the room cost is $2700 for a double and
// $3500 for a single
DormRoom::DormRoom(int roomNo, int number):
roomNumber(roomNo), roomCount(number)
{
if (roomCount == 2)
roomCost = 2700.0;
else
roomCost = 3500.0;
}
// return room number
int DormRoom::getRoomNumber()
{
return roomNumber;
}
// return number of room occupants
int DormRoom::getRoomCount()
{
return roomCount;
}
// return the cost of the room
double DormRoom::getRoomCost()
{
return roomCost;
}
// change the number of students in the room. must recompute
// the room cost
void DormRoom::setRoomCount(int number)
{
roomCount = number;
if (roomCount == 2)
roomCost = 2700.0;
else
roomCost = 3500.0;
}
#endif // DORM_ROOM_CLASS
update: this is the outline we are to use for this program
int main( )
{
// COMMENT THE OBJECTS (ABOVE THE CODE)
// DECLARE THE OBJECTS (EACH OBJECT SHOULD BE DECLARED ON A SEPARATE LINE FOR READABILITY)
// OUTPUT A MESSAGE TO THE USER (cout) - REFER TO THE RUN SECTION IN THE PROGRAM DIRECTIONS
// INPUT THE RESPONSE FROM THE USER (cin)
// COMMENT THE CONSTRUCTORS (ABOVE THE CODE)
// DECLARE THE CONSTRUCTORS (EACH CONSTRUCTOR SHOULD BE DECLARED ON A SEPARATE LINE FOR READABILITY)
// DECLARE THE CONSTRUCTOR FOR EACH OF THE OBJECTS: specialA, specialB, and specialC
// FOR EXAMPLE, DormRoom specialA(1, numberInRoom1);
// COMMENT THE CALCULATION (ABOVE THE CODE)
// PERFORM THE CALCULATION FOR totalFunds
// NOTE: totalFunds SHOULD BE DELCARED AT THE TOP OF THE PROGRAM WITH THE OTHER OBJECTS.
// FOR EXAMPLE, totalFunds = specialA.getRoomCount( ) * specialA.getRoomCost( ) + .....
// CONTINUE WITH THE REMAINING OBJECTS: specialB and specialC
// OUTPUT THE INFORMATION - REFER TO THE RUN SECTION IN THE PROGRAM DIRECTIONS. DO NOT FORGET THE $ AND
// AND TO USE SETREAL FOR THE DECIMAL PLACE.
return 0;
}
Thanks for the help everyone.
It looks to me like this might be confusion with the word "declare." Your teacher says declare the constructors, then gives the example DormRoom specialA(1, numberInRoom1);. I believe he/she technically means "declare with the constructor". The constructor itself has already been declared in the header file, and you don't absolutely need a default constructor unless your inheriting from DormRoom.
You have a declared constructor of DormRoom in your dormroom.h header file. You don't need to declare in any other place. In main function you only create new objects, using a constructor declared before.
By the way, it is a good practice to not to add an implementation of functions, classes etc int header files. You should divide into two separate .cpp and .h files. In header file should be only declarations and in source file (.cpp) should be only an implementation.
In some new territory working with pointers and references, I am attempting to pass an array by reference into a function using a pointer, however I keep getting errors no matter what I try, I am sure the problem is very simple to fix but I just cant seem to wrap my head around it, can anyone see the mistake im making? any help will go a long way thanks
#include<iostream>
#include<cmath>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <new>
using namespace std;
//Inline function
inline double getFahrenheit(double theCelsius)
{
//Convert the celcius to farenheit
return (theCelsius + 32) * 5 / 9;
}
void outputWeather(double *temperaturesArray, const string WEEK_DAY_NAMES[], const double MAX_NUMBER)
{
//this is a counter that will increment through the days and various
int counter;
//reset the counter to 0 so we can use it again
counter = 0;
//print a header
cout << "THIS WEEKS TEMPERATURE REPORT " << endl;
//print a divider
cout << "=============================" << endl;
//while the counter is less than 7 repeat again
while(counter < MAX_NUMBER)
{
//print out the temperatures by day
cout << WEEK_DAY_NAMES[counter] << " " << temperaturesArray[counter] << "\370C " << getFahrenheit(temperaturesArray[counter]) <<"\370F "<< endl;
//increase the counter by 1
counter +=1;
}
}
//Function that will determine whether or not the value the user entered was numeric and within the range
double checkValidation(string weekDay)
{
//Create a variable to store a valid number
double validNumber;
//This will hold the value for the lowest
const double MIN_NUMBER = 1;
//This will hold the value for the highest temperature
const double MAX_NUMBER = 365;
//This will hold the value for the valid number that the user will eventually enter
validNumber = 0.0;
//This will alert the user to enter a temperature for that day of the week
cout << "Please enter the temperature for " << weekDay << endl;
//This will take in teh value the user entered for teh temperature
cin >> validNumber;
//If the text the user entered was not numeric start again
if(cin.fail())
{
//C++ built in methods for clearing the cin
cin.clear();
fflush(stdin);
//alert the user what they typed was wrong
cout << "invalid input. please try again and enter a numeric value" << endl;
//pass in the weekeday and start over
checkValidation(weekDay);
}
else
{
//if teh number falls outside the range
if(validNumber < MIN_NUMBER || validNumber > MAX_NUMBER)
{
//Alert the user that it was outside the range
cout << "invalid input. please try again and enter a value between -90 and 60" << endl;
//pass in the weekday and try again
checkValidation(weekDay);
}
}
//return the valid number
return validNumber;
}
int main()
{
//this is a counter that will increment through the days and various
int counter;
//a constant to hold the variable for the number of days
const int MAX_COUNTER = 7;
//an array that will hold all the days of the week
const string WEEK_DAY_NAMES[] =
{
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
//this will hold all of teh temperatures
double temperaturesArray[MAX_COUNTER];
//start the counter off at 0
counter = 0;
//begin telling the user to enter temperatures by printing a header
cout << "Please enter the temperature for every day of the week " << endl;
//while the counter is less than 7 we will repeat
while(counter < MAX_COUNTER)
{
//add temperature to the array
temperaturesArray[counter] = checkValidation(WEEK_DAY_NAMES[counter]);
//add 1 to the counter
counter +=1;
}
double * arrayPointer = new double[MAX_COUNTER];
arrayPointer = &temperaturesArray;
outputWeather(arrayPointer, WEEK_DAY_NAMES, MAX_COUNTER);
system("PAUSE");
return 0;
}
In C++, the size of an array is encoded into its type.
There is no general "array of doubles" type. But there is an "array of 7 doubles" type, and an "array of 13 doubles" type, and so on.
So to pass an array as an array, and not simply as a pointer, to a function, you need to encode the precise type in the function's signature.
It won't be "a function which takes an array", but "a function which takes an array of size 7".
The way to do that is as follows:
void f(double (&arr)[7]);
Or of course, you can template it, if the array size is not fixed:
template <size_t N>
void f(double (&arr)[N]);
But really, what you're trying to do shouldn't be done using raw arrays at all.
Use the standard library vector.
Briefly, replacing line
arrayPointer = &temperaturesArray;
with
arrayPointer = temperaturesArray;
makes the code to compile.
Notice that arrayPointer is of type double* and temperaturesArray is of type double[MAX_COUNTER] (with MAX_COUNTER = 7). Hence, you can assign arrayPointer to the address of a double but you cannot assign arrayPointer to the address of a double[MAX_COUNTER]. That's what the original code attempted to do and thus, it failed to compile.
On the other hand, each element of a double[MAX_COUNTER] is a double. In particular, the first element is a double and you can assign its address to arrayPointer:
arrayPointer = &temperaturesArray[0];
The fix above is just a synctatic sugar for this line. Indeed, when you assign an object of type "array of type T" (e.g. double[MAX_COUNTER]) to a "pointer of type T", then the compiler performs the so called array-to-pointer conversion which means that is assigns the address of the first array element to the pointer.
Now a little remark on your code (with the provided fix), specifically, the following lines:
double * arrayPointer = new double[MAX_COUNTER];
arrayPointer = temperaturesArray;
The first line above allocates heap memory to store an array of MAX_COUNTER objects of type double. Then the address of the first element of this array is assigned to arrayPointer.
Then, the following line reassigns arrayPointer to the address of the first element of temperaturesArray. Therefore, the address of the first element of the heap allocated array is lost and you can no longer delete it. Remeber that every call to new must be matched by a call to delete (otherwise you have a memory leak). In this particular case, however, the best thing to do isn't call delete. Actually, you should eliminate the call to new since the heap memory is never used. More precisely, you can remove the first line above.
How do I get past this variable initialization problem? If I only could figure out how to only initialize them only once...
* Main.cpp : main project file.
/************************** Begin Header **************************/
#include "stdafx.h" //Required by Visual C ++
#include <string> //Required to use strings
#include <iostream> //Required for a bunch of important things
#include <iomanip> //Required for manipulation of text and numbers
using namespace System; // Automatically uses System namespace
using namespace std; // Automatically uses std namespace
#pragma hdrstop // Stops header here
/*************************** End Header ***************************/
//* Begin Function Headers *//
void inputData(); // This will be used to organize class member calls when setting and getting new data.
int getData(); // Will get user data, input in character string, convert to an integer and then perform data validation.
void createReport(int place, int number, string type); // Will organize commands to create the report and display it on the screen.
//* End Function Headers *//
class JarsSold // Begin Class -- JarsSold
{
/* Begin Initialization & Creation of important resources */
private:
static const int MaxArray = 5; // Value for the size of array JARS_SOLD
int JARS_SOLD[MaxArray]; // Creation of array with size of MaxArray
/* End Initialization & Creation of important resources */
public: // Makes underlining elements Public instead of the default Private
JarsSold() // Begin Constructor
{ // Put something in num array
JARS_SOLD[0] = 0; // [1]
JARS_SOLD[1] = 0; // [2]
JARS_SOLD[2] = 0; // [3]
JARS_SOLD[3] = 0; // [4]
JARS_SOLD[4] = 0; // [5]
} // End Constructor
~JarsSold(){}; // Destructor
/* Put all members for JarsSold class below here */
void setNumber(int num, int value) // Set the number of jars sold with number placement in array and value to replace it with
{
JARS_SOLD[num] = value; // Stores value into JARS_SOLD at whatever num is at the time
}; // End setNumber class member
int getNumber(int num) // Get the current number held for jars sold with number placement in array
{
return JARS_SOLD[num]; // Returns whatever is in JARS_SOLD depending on what num is at the time
} // End getNumber class member
/* Put all members for JarsSold class above here */
}; // End Class -- JarsSold
class SalsaTypes // Begin Class -- SalsaTypes
{
/* Begin Initialization & Creation of important resources */
private:
static const int MaxArray = 5; // Value for the size of array JARS_SOLD
string SALSA_TYPES[MaxArray]; // Creation of array with size of MaxArray
/* End Initialization & Creation of important resources */
public: // Makes underlining elements public instead of the default Private
SalsaTypes() // Begin Constructor
{ // Add default strings to str array
SALSA_TYPES[0] = "Mild"; // [1] Stores Mild into SALSA_TYPES at 0 spot
SALSA_TYPES[1] = "Medium"; // [2] Stores Medium into SALSA_TYPES at 1 spot
SALSA_TYPES[2] = "Sweet"; // [3] Stores Sweet into SALSA_TYPES at 2 spot
SALSA_TYPES[3] = "Hot"; // [4] Stores Hot into SALSA_TYPES at 3 spot
SALSA_TYPES[4] = "Zesty"; // [5] Stores Zesty into SALSA_TYPES at 4 spot
} // End Constructor
~SalsaTypes(){}; // Destructor
/* Put all members for SalsaTypes class below here */
void setType(int num, string type) // Set salsa type with number placement in array and string value to replace with
{
SALSA_TYPES[num] = type; // Stores the string type into SALSA_TYPES at whatever num is at the time
}; // End setType class member
string getType(int num) // Get the Salsa Type with number placement in array
{
return SALSA_TYPES[num]; // Returns SALSA_TYPES depending on what is in num at the time
}; // End getType class member
/* Put all members for SalsaTypes class above here */
};// End Class -- SalsaTypes
void main( void ) // Begin Main Program
{
cout << fixed << setprecision(1) << setw(2); // Do a little customization with IoManip, might as well, we just might need it
// Main Program Contents Begin Here //
// Opening Credits for Program
cout << "Welcome to the /Professional Edition\\ of the Chip and Salsa Sale Tool EXPRESS." << endl;
cout << "This handy-dandy tool will make a chip and salsa manufacturer's job much easier!" << endl;
cout << endl << endl << endl;
cout << "Press any key to begin inputing the number of jars sold for these salsa flavors: " << endl << endl;
cout << "-Mild" << endl << "-Medium" << endl<< "-Sweet" << endl << "-Hot" << endl << "-Zesty" << endl << endl << endl;
system("pause"); // Pause here. After this begin data input
cout << endl << endl << endl;
inputData(); // Will deal with data input, validation, and reports
// Main Program Contents End Here //
} //End Main Program
// All Content for Functions Begin Here //
void inputData() // Begin inputData Function
{
// Begin Create Class Obects //
SalsaTypes salsa;
JarsSold jars;
// End Create Class Objects //
// Variable Creation Begin //
// Variable Creation End //
// All Content for Functions Begin Here //
for (int i = 0 ; i < 5 ; i++) // Start For Loop
{
cout << "Input how many Jars were sold for \"" << salsa.getType(i) << "\"" << ": "; // Displays which Salsa we are reffering to
jars.setNumber(i,getData()); // Will use getData() to determine what value to send to the JarsSold class.
createReport(i,jars.getNumber(i),salsa.getType(i)); // Send these numbers to another function so it can make a report later
cout << endl << endl; // Using this as a spacer
}
// All Content for Functions End Here //
}; // End inputData Function
int getData() // Begin getData Function
{
// Variable Creation Begin //
char charData[40]; // Will be used to store user data entry
int numTest; // Will be used for Data Validation methods
// Variable Creation End //
// Main Contents of Function Begin Here //
retry: // Locator for goto command
cin >> charData; // Ask user for input. Will store in character string then convert to an integer for data validation using 'Atoi'
numTest = atoi ( charData ); // Convert charData to integer and store in numTest
if (numTest < 0) { numTest = 0; cout << endl << endl << "You can't enter negative numbers! Try Again." << endl << endl << "Re-enter number: "; goto retry;} // Filter out negative numbers
// Main Contents of Function End Here //
return numTest; // If it makes it this far, it passed all the tests. Send this value back.
}; // End getData Function
void createReport(int place, int number, string type) // Begin createReport Function
{
// Variable Creation Begin //
int const MAX = 5; // Creat array size variable
int lowest; // The integer it will use to store the place of the lowest jar sales in the array
int highest; // The integer it will use to store the place of the highest jar sales in the array
int total; // The integer it will use to store total sales
int numArray[MAX]; // Create array to store jar sales (integers)
string typeArray[MAX]; // Create array to store types of salsa (strings)
// Variable Creation End //
// Main Contents of Function Begin Here //
numArray[place] = number; // Store number into new array
typeArray[place] = type; // Store type into new array
if (place = 4) // If the array is full it means we are done getting data and it is time to make the report.
{ // Begin making report, If statement start
for ( int i = 0 ; i < 5 ; i++ ) // Using a for-loop to find the highest and lowest value in the array
{ // For Loop to find high and low values BEGIN
if ( lowest < numArray[i]) // Keep setting lowest to the lowest value until it finds the lowest one
{ // Start If
lowest = numArray[i]; // Lowest equals whatever is in numArray at i spot
} // End If
if ( highest > numArray[i]) // Keep setting highest to the highest value until it finds the highest one
{ // Start If
highest = numArray[i]; // Highest equals whatever is in numArray at i spot
} // End If
total += numArray[i]; // Will continually add numArray at i spot until it has the total sales
} // For Loop to find high and low values END
// Main Contents of Function End Here //
} // END creatReport Function
// All Content for Functions Ends Here //
Well my problem is...I need to get my data across from one function to another. I thought I could figure out how to create Global Class Objects but I couldn't. So I thought I could get around just passing the arguments to another function and then restoring them in its own arrays and then keep doing that until i've completely copied all number arrays and string arrays. Well...yeah that does work EXCEPT this part in createReport():
// Variable Creation Begin //
int const MAX = 5; // Create array size variable
int lowest; // The integer it will use to store the place of the lowest jar sales in the array
int highest; // The integer it will use to store the place of the highest jar sales in the array
int total; // The integer it will use to store total sales
int numArray[MAX]; // Create array to store jar sales (integers)
string typeArray[MAX]; // Create array to store types of salsa (strings)
// Variable Creation End //
What happens is, I guess I am so tired that I missed it, every time I call that function it re-initializes those same variables again. I will put variables into another variable and then it will get re-initialized to default values.
I tried using a counter variable that counted to one after it initialized and then after it was 1 it wouldnt initialize again. No that didn't work because the variables werent initialized outside the scope of the if statement. I then tried a GOTO statement that would skip the initialization after it happened once. Nope something went wrong with the first initialization phase and no compilation.
I need to figure out how I can either
keep those variables from getting re-assigned or initialized so they can maintain their values. Or
figure out how to create global class objects (and yes I've tried the extern classes with multiple source files. No luck just lots of errors)
I'm not very good at programming yet. But I assure you I've been working on this piece for hours and hours and hours and I stayed up all night just constantly trial and error. I really proud of myself because this code is pretty advanced for me. I've looked at every tutorial on Google and I'm fresh out of luck -- you guys are my last hope!! Sorry again guys. I know this is a dumb question...
One last quick question. How do you create Global Class Objects? For example
MyClass
{
MyClass(){};
~MyClass(){};
}
MyClass MyClassObject;
How do I use MyClassObject throughout my entire program?
The only way I can use it is if I create a new object every time with every function I use. And that means I lose all data stored inside my class?
I've read that having global object isn't a good idea though. I would love to not have to use them but I have no clue on any alternatives that I can actually understand.
Any other critiques or tips is GREATLY appreciated. I love this stuff I just don't have many people to ask questions to.
You're doing really well! The simple answer is to write static in front of your variables:
static int const MAX = 5; // Creat array size variable
static int lowest; // The integer it will use to store the place of the lowest jar sales in the array
static int highest; // The integer it will use to store the place of the highest jar sales in the array
static int total; // The integer it will use to store total sales
static int numArray[MAX]; // Create array to store jar sales (integers)
static string typeArray[MAX]; // Create array to store types of salsa (strings
I think I can give you better advice though, I'll look at your code for a bit longer. As for global variables, what you wrote there with MyClassObject would work fine. You'd use it like this:
MyClass {
public:
MyClass(){};
~MyClass(){};
int variable;
};
MyClass MyClassObject;
// in a method you'd do this
void whateverMethod() {
MyClassObject.variable = 5;
std::cout << MyClassObject.variable << std::endl;
}
That kind of thing. There are some style issues that could be fixed but to be honest I say just get it working first and then we can talk about those.
1) Make them static eg
static string typeArray[MAX];
Initialise them once, they'll remain the same until you change them. Don't try and use them at the same time from two threads.
2) You can create a global object by declaring it at file scope:
class CFoo;
CFoo s_foo;
class CFoo
{
public:
CFoo();
~CFoo();
}
Then s_foo is going to be an instance of CFoo available anywhere which can see s_foo (this file and others which have extern CFoo s_foo;)