I was asked to use try-catch exception handling to handle the case if the list of students is empty. I'm new to exception handling can someone guide me? Below is the implementation of average method. I need to use try-catch to make sure the list isn't empty.
double classAverage()
{
int count = 0;
double sum = 0.0;
Container *temp = list;
if (temp == NULL)
{
cout << "List empty!";
return -1.0;
}
while (temp != NULL)
{
sum += temp->student->getGrade();
temp = temp->next;
count++;
}
return sum / count;
}
Since changeGrade is a friend of Student, this should work:
void changeGrade(Student* s, int newGrade) {
s->grade = newGrade;
}
Right now you repoint the pointer s to a new Student object. What you want
is to change the object that was passed in to the function.
There isn't enough information, but I think your code might look like this:
Student* student = new Student("John", "Doe", 99, 1);
changeGrade(student, 9);
This won't change the student at all, because you pass the pointer by value, so when you reassign it, the original object (the pointer student) is not changed. For the calling code to notice a change you have to pass by reference (Student*& - recommended way) or using a pointer to a pointer (Student**);. Also, don't forget to delete the original pointer (Student*) if you are using the Student** approach, or you'll leak memory.
You shouldn't make a way around not setting the int, its probably bad practice and confusing.
Related
I have an assignment where we need to use this basic structure of vectors and classes to learn about parent and child classes and polymorphism. Here is the code of the function I'm supposed to write:
void assignStudents(vector<Student*>& v) {
for (int i = 0; i < 5; i++)
{
cout << "Enter a study level: ";
string input;
cin >> input;
if (input == "graduate")
{
Graduate inputClass;
Student* inputParentClassPtr = &inputClass;
v.push_back(inputParentClassPtr);
v[i]->addToVector(input);
inputParentClassPtr = nullptr;
}
else if (input == "undergraduate")
{
Undergraduate inputClass;
Student* inputParentClassPtr = &inputClass;
inputParentClassPtr->addToVector(input);
v.push_back(inputParentClassPtr);
}
else
{
cout << "Please enter a valid response, either graduate or undergraduate" << endl;
i--;
}
}
for (size_t i = 0; i < v.size(); i++)
{
vector<string> studyLevels = v[i]->getStudyLevels();
size_t size = studyLevels.size();
for (int j = 0; j < size; j++)
{
cout << studyLevels[j];
}
}
}
I debug the program and every time the first for loop moves on to the next iteration, every member variable inside each object in my vector goes blank, but then when I add a new object into the vector, then call the addToVector() function they come back.
I added the bottom for loop to check if any editing is happening, and once I get to that bottom for loop, every member variable is empty again.
I have the Student class vector where I am adding Undergraduate and Graduate classes to. Every Student class has a protected vector inside called levels. I need to add the class to vector that holds all my objects, then edit the member variable vector to include the string representing the type of class it is.
Why do the member variables (levels) go blank every time it finishes an iteration of the for loop?
I'll just focus on one part, as the same issue appears twice in your code.
{
Graduate inputClass; // create local student "on the stack"
Student* inputParentClassPtr = &inputClass;
v.push_back(inputParentClassPtr); // store address of student
v[i]->addToVector(input);
inputParentClassPtr = nullptr; // has no real effect
} // inputClass goes out of scope and is destroyed here
When the block ends, the local "stack" variables from that block are destroyed. That means the Graduate object is no longer valid, and the pointer you stored in v is now pointing at something unusable.
To fix that, you need to create the objects in dynamic memory.
You should change your vector to store std::unique_ptr<Student>, and create the objects using std::make_unique(), like this:
auto inputParentClassPtr = std::make_unique<Graduate>();
v.push_back(std::move(inputParentClassPtr));
But, if you can't do that, you will need to use new instead, like this:
Student* inputParentClassPtr = new Graduate();
v.push_back(inputParentClassPtr);
Either way, even though inputParentClassPtr is still destroyed at the end of the block, it is only a pointer and the Graduate object it pointed to is still alive.
If you use new, you'll then need to delete all the objects in the vector when you are done using them, or you'll have a memory leak. Using std::unique_ptr will handle that for you.
I am currently trying to create a linked list which has two elements, usernames and seconds. It is supposed to read from a file and save it into two vectors.
I'm not sure why, but when I attempt to collect the data and store it into a linked list, I get a segmentation fault.
I'm kind of in a rutt, I feel like this should work.
Here is my code for main.cpp:
// main.cpp
int main() {
//Collect initial leaderboard data into two parallel vectors
cout << "here";
vector<string> playerNames;
vector<unsigned> playerTimes;
collect_data(playerNames, playerTimes);
cout << "here";
//Create a LeaderBoard object based on the data in the parallel vectors
LeaderBoard players(playerNames, playerTimes);
cout << "Initial leaderboard from https://www.speedrun.com/ac#All_Debts\n";
players.display();
cout << endl;
return 0;
}
//Leaderboard.cpp
LeaderBoard :: LeaderBoard(const vector<string>& usernames, const vector<unsigned>& second) //Combines both vectors to linked list;
{
for (int i = 0; i < usernames.size(); i++)
{
nPlayers_ ++;
Player *ptr = new Player;
ptr = nullptr;
ptr->username = usernames[i];
ptr->seconds = second[i];
if (head_ == nullptr)
{
head_ = ptr;
tail_ = ptr;
}
else
{
while (tail_-> next != nullptr)
{
tail_ = tail_ -> next;
}
tail_->next = ptr;
tail_ = ptr;
}
}
}
Can someone help me, or lead me towards the right direction?
In this part
Player *ptr = new Player;
ptr = nullptr;
ptr->username = usernames[i];
ptr->seconds = second[i];
You are overwriting the pointer to newly created object by nullptr, then dereferencing the nullptr. This will lead to memory leak and Segmentation Fault.
The line
ptr = nullptr;
should be removed from here.
Also it seems you forgot to initialize ptr->next.
ptr->next = nullptr;
should be added after that part.
This code presents some issues.
First of all, you might ditch heap allocation on each iteration of the for loop, using only once the allocation, before entering the loop. This prevents errors such as:
Player *ptr = new Player;
ptr = nullptr;
which causes segmentation fault in your code.
Another problem might be that head_ and tail_ might also be nullptr, so you have to check carefully about both.
You are allocating something on the heap, without caring about deleting the data later.
Why don't you use a std::vectorstd::unique_ptr<Player> to collect all the player scores, so at the end of the program, everything will be deleted?
//Assuming there's a vector like this in Leaderboard.h:
#include <memory>
std::vector<std::unique_ptr<Player>> players_{};
//Leaderboard.cpp
LeaderBoard::LeaderBoard(const vector<string>& usernames, const vector<unsigned>& second) //Combines both vectors to a final list;
{
for (int i = 0; i < usernames.size(); i++)
{
Player p{};
p.username = usernames[i];
p.seconds = second[i];
players_.emplace_back(std::move(Player));
}
}
In this way, you ditch tricky pointer handling problems, allocation problems, segmentation fault all together. As nice result, you have a vector that can be used with a broad range of algorithms
When the below code is run, I get garbage output. I have debugged it enough to figure out the error comes when I try to access hobbies[i]->hobby. Any help would be appreciated. I have been trying to figure out what is going on for hours.
int Graph::addUserToHobby(std::string hobby, std::string id){
int key = ((int)hobby[0] + (int)hobby[1])%HASHMAP_SIZE;
int collisions = 0;
while(hobbies[key] != NULL && hobbies[key]->hobby.compare(hobby) != 0 ){
key++;
collisions++;
if(key >= HASHMAP_SIZE){
key = 0;
}
}
if(hobbies[key] == NULL){
hobbylist hob;
hob.hobby = hobby;
hob.list.push_back(findVertex(id));
hobbies[key] = &hob;
}
else{
hobbies[key]->list.push_back(findVertex(id));
}
return collisions;
}
void Graph::displayHobbies(){
for(int i=0; i<HASHMAP_SIZE; i++){
if(hobbies[i] != NULL){
cout << hobbies[i]->hobby << ": ";
for(unsigned int j=0; j<hobbies[i]->list.size()-1; j++){
cout << hobbies[i]->list[j]->name << ", ";
}
cout << hobbies[i]->list[hobbies[i]->list.size()-1]->name << endl;
}
}
}
Focus your attention in that part of the code:
if(hobbies[key] == NULL) {
hobbylist hob;
...
hobbies[key] = &hob;
}
When hob gets out of scope (at the end of that if-statement's body), hobbies[key] will reference something that doesn't exist any more.
Later on in your program, as you correctly noticed, when you do cout << hobbies[i]->hobby;, you will request for hobby on something that has gone out of scope, which invokes Undefined Behavior (UB).
Some possible solutions:
Use an std::map, instead of the array of pointers you use
now. The container will automatically take care of the memory
management for you. (Recommended)
Use smart pointers (e.g. std::unique_ptr), instead of raw pointers. Read more in
What is a smart pointer and when should I use one?
Dynamically allocate hob, so that its lifetime is extended (that
means that when that if-statement's body terminates, hob's
lifetime won't terminate). This approach requires you to be
responsible for the memory management (you have to de-allocate every
piece of memory that you dynamically allocated before (call delete
as many times as you called new)).
In this part:
if(hobbies[key] == NULL){
hobbylist hob;
/* ... */
hobbies[key] = &hob;
}
hob is allocated on the stack and deleted after the if block. So the pointer you have in hobbies[key] is dangling. You can catch these sort of errors with valgrind.
Your issue is that you are populating your hobbies value with pointers to objects allocated on the stack.
These objects will have subsequently been destroyed. Perhaps you were meaning to allocate them on the heap with new?
hobbylist* hob = new hobbylist;
...
hobbies[key] = hob
bool StudentList::remove(const char * studentName)
{
for (int i = 0; i < MAX_SIZE; i++)
{
if (this->students[i]->isEqualTo(studentName)) // Finds a name to remove
{
cout << "Remove: "; // Displays name wished to be removed
students[i]->print();
// delete[] students[i]; - Crashes
// students[i] = NULL; - Replaces removed name with null, stops working.
// students[i]->~Student(); - Call deconstructor, Crashes.
return true;
}
}
return false;
}
I just want to remove a single element out of the array, but keeps crashing when i delete that element.
students[i] is a pointer array, and i need to remove selected elements
First quetion, if you really need to delete "Student" object. If yes, you can add some bad code like:
students[i] = nullptr;
If your students are stored not only in this array, you can make that storage responsible for their deleting. But both ways aren't very good because of using null pointers later. Learn how to use collections, for example vector. You will be able just remove the pointer from an array.
It seems that you want to delete each instance of students, if you could find the studentname.
students seems a two dimensional structure pointer to a pointer. ie; **students. But, you are deleting it in wrong way.As you first need to delete the instance of students[i], then delete the instance of students.
Also, since you are calling the destructor students[i]->~Student(); after deleting instance, it may crash again, as you have assigned student[i] = NULL. then it will be, NULL->~Student() -it will also lead crash.
You need to delete it in following way :
for (int i = 0; i < MAX_SIZE; i++)
{
if (this->students[i]->isEqualTo(studentName)) // Finds a name to remove
{
students[i]->~Student();
delete students[i];
students[i] = NULL;
}
}
delete[] students;
students = NULL;
Guessing I'm doing something stupidly simple wrong, but can't seem to find an answer in existing stack overflow questions. I'm trying to implement a simple hash table containing lists of strings in C++ for a programming class. My add() function appears to be working correctly from inside the function, but as soon as I check the hash table's contents from the contains() function it's obvious that something's gone wrong.
void string_set::add(const char *s) {
//copy s into new char array str
char str[strlen(s)];
strcpy(str, s);
//find hash value of string
int hValue = string_set::hash_function(s);
//create new node to contain string
node* newNode = new node();
newNode->s = str;
//if string's position in hash table is empty, add directly and
//set newNode's next to null. if not, set newNode next to
//current first node in list and then add to hash table
if(hash_table[hValue] == NULL) {
hash_table[hValue] = newNode;
newNode->next = NULL;
} else {
newNode->next = hash_table[hValue];
hash_table[hValue] = newNode;
}
cout << "string added: " << hash_table[hValue]->s << endl;
return;
}
This prints the expected string; i.e. if I add "e" it prints "e".
But when I call this immediately after:
int string_set::contains(const char *s) {
//find hash value of string
int hValue = string_set::hash_function(s);
//return inital value of hash table at that value
cout << "hash table points to " << hash_table[hValue]->s << endl;
}
It prints some junk. What have I done?
Since this is for a class, the specifications have been provided and I have no opportunity to change the way the hash table is set up. I'll be adding exceptions etc later, just want to get the add function working. Thanks!
EDIT: Sorry, new to stack overflow and not sure about comment formatting! Yes, I can use std::string. The hash function is as follows
int string_set::hash_function(const char *s) {
int cValue =0;
int stringSum = 0;
unsigned int i = 0;
for(i = 0; i < strlen(s); i++) {
cValue = (int) s[i];
stringSum = stringSum + cValue;
}
stringSum = stringSum % HASH_TABLE_SIZE;
return stringSum;
}
You are trying to use local variable outside of its function scope. This is an undefined behavior in C++. In your compiler realization, stack frame is invalidated, so all newNode->s pointers became dangling, memory, they are pointing, is already used to store different stack frame. To solve this issue you could either dynamically allocate memory on the heap or use std::string instead of char* which is the best approach.
Also its worth pointing out, that standard C++ library already have hash table realization std::unordered_map.