Class with Pointer and Dynamic Arrays - c++

I'm currently writing a program that will help a college keep track of which students are in what clubs. This code will be pasted into existing code that works, as provided by my professor.
Could someone please look my code over, and help me work through my mistakes? I included comments to explain what everything should do.
Also, please note this is an assignment and I cannot change anything about the class. I'm also a beginner, so keep it simple, please. Your help will be extremely appreciated beyond all measure.
class Club
{
public:
Club();
Club(Club &c);
Club(string cname);
void addMember(string name);
void removeMember(string name);
string getClubName() const;
string setClubName(const string& nameOfClub);
void loadClub();
bool isMember(string& name) const;
string getAllMembers() const;
friend Club mergeClubs(Club& c1, Club& c2);
~Club();
private:
string *members;
int numMembers;
string clubName;
};
Club::Club()
{
clubName = "";
numMembers = 0;
}
Club::Club(Club &c)
{
numMembers = c.numMembers;
members = new string [numMembers];
for (int i = 0; i < numMembers; i++)
{
members[i] = c.members[i];
}
//copy constructor
//watch out for memory leaks
}
Club::Club(string cname)
{
clubName = cname;
//cname should be saved as the club's name in the clubName variable
}
void Club::addMember(string name)
{
string *m;
m = new string [numMembers];
string *members = m;
for (int i = 0; i < numMembers; i++)
{
for (int j = 0; j < name.length(); i++)
{
m[i] = name[j];
}
}
delete [] m;
//adds new member to the club
//records their name in the members variable
//may need a dynamic array to make this work, watch for memory leaks!
}
void Club::removeMember(string name)
{
string *m;
m = new string [numMembers];
string *members = m;
for (int i = 0; i < numMembers; i++)
{
if ( m[i] == name)
{
m[i] = "";
}
}
delete [] m;
//deletes the person from the array in members
//will do nothing if the person is not in the array to begin with
//may require dynamic array to make this work- watch for memory leaks!
//if the person's name appears more than once, just delete the first instance
}
string Club::getClubName() const
{
return clubName;
//getter of clubName
}
string Club::setClubName(const string& nameOfClub)
{
return clubName = nameOfClub;
//setter of clubName
}
void Club::loadClub()
{
//should print "tell me the name of the next member of the club"
//reads into the variable name
//uses addMember() to add that person to the club
//the input should include up to the line break as the name, so it should take in "jane doe" as an entry
//keeps asking for more names until the user enters a blank entry
string name;
do
{
cout << "Tell me the name of the next member of the club, ";
cout << "or submit a blank entry to stopent ering names." << endl;
getline(cin, name, '\n');
addMember(name);
} while (name != "");
}
bool Club::isMember(string& name) const
{
/*for (int i = 0; i < numMembers; i++)
{
for (int j = 0; j < name.length(); j++)
{
if (members[i] == name)
{
return true;
}
else
{
return false;
}
}
}*/
for (int i = 0; i < numMembers; i++)
{
if (members[i] == name)
{
return true;
}
return false;
}
//returns true if the person is a member of the club, false otherwise
}
string Club::getAllMembers() const
{
for (int i = 0; i < numMembers; i++)
{
return members[i];
cout << ", ";
}
cout << endl;
//returns a string of all the names of the members of the club
//commas and spaces separating every entry of the list
//should not be a comma following the last name in the list
}
Club mergeClubs(Club& c1, Club& c2)
{
//creates a new club from 2 existing clubs
//combined club name should be Club 1/Club 2
Club temp;
temp.clubName = c1.clubName + "/" + c2.clubName;
return temp;
}
Club::~Club()
{
delete [] members;
//destructor
//watch out for memory leaks
}

For some reason I cannot fathom I have (mostly) corrected this program for you. There are still some nasty things like passing copies of strings into const functions but fixing these would be mere optimisations. At least this program is logically correct.
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
using namespace std;
class Club
{
public:
Club();
Club(const Club &c);
Club(string cname);
void addMember(string name);
void removeMember(string name);
string getClubName() const;
string setClubName(const string& nameOfClub);
void loadClub();
bool isMember(const string& name) const;
string getAllMembers() const;
friend Club mergeClubs(Club& c1, Club& c2);
~Club();
private:
vector<string> members;
string clubName;
};
Club::Club()
: members()
, clubName()
{
}
Club::Club(const Club &c)
: members(c.members)
, clubName(c.clubName)
{
}
Club::Club(string cname)
: members()
, clubName(cname)
{
}
void Club::addMember(string name)
{
members.push_back(name);
}
void Club::removeMember(string name)
{
members.erase(remove(members.begin(), members.end(), name), members.end());
}
string Club::getClubName() const
{
return clubName;
//getter of clubName
}
string Club::setClubName(const string& nameOfClub)
{
return clubName = nameOfClub;
//setter of clubName
}
void Club::loadClub()
{
//should print "tell me the name of the next member of the club"
//reads into the variable name
//uses addMember() to add that person to the club
//the input should include up to the line break as the name, so it should take in "jane doe" as an entry
//keeps asking for more names until the user enters a blank entry
string name;
do
{
cout << "Tell me the name of the next member of the club, ";
cout << "or submit a blank entry to stopent ering names." << endl;
getline(cin, name, '\n');
addMember(name);
} while (name != "");
}
bool Club::isMember(const string& name) const
{
return find(members.begin(), members.end(), name) != members.end();
}
string Club::getAllMembers() const
{
stringstream result;
vector<string>::const_iterator b = members.begin(), e = members.end();
for (bool comma = false ; b != e; ++b, comma = true)
{
if (comma) {
result << ", ";
}
result << *b;
}
return result.str();
}
Club mergeClubs(Club& c1, Club& c2)
{
Club temp(c1.clubName + "/" + c2.clubName);
struct memberAdd {
Club& _club;
memberAdd(Club& club) : _club(club) {}
void operator()(const string& member) {
_club.addMember(member);
}
};
for_each(c1.members.begin(), c1.members.end(), memberAdd(temp));
for_each(c2.members.begin(), c2.members.end(), memberAdd(temp));
return temp;
}
Club::~Club()
{
//destructor
//watch out for memory leaks
}
int main()
{
Club boys("red");
boys.addMember("Ben");
boys.addMember("Paul");
Club girls("blue");
girls.addMember("Lucy");
girls.addMember("Hermione");
Club unisex = mergeClubs(boys, girls);
cout << unisex.getClubName() << " has the following members: " << unisex.getAllMembers() << endl;
}

Some initial comments:
The copy constructor should take a const Club& like this:
Club(const Club& club)
members and numMembers are anachronistic. Use a std::vector for members and drop the numMembers altogether (std::vector has a size() method).
Club::Club does not initialise members or numMembers. This will result in a possible crash in the destrutor. This would be solved if you replaced them with a vector.
the logic in addMember does not make sense.
nor in removeMember
isMember should take a const string& and you would be well advised to write it in terms of std::find() (#include <algorithm>)
I could go on...

Related

Sorting class array in ascending order

Quite new to programming and in need of some help, I'm looking to sort an class array in ascending order based on age, but I can only get the code to work in descending order. I may be overlooking something small since I've worked on this far too long so I'd appreciate any help!
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string name;
int age;
Person(string name = "empty", int age = 0)
{
setName(name);
setAge(age);
}
void setName(string x) {
name = x;
}
string getName() {
return name;
}
void setAge(int y) {
age = y;
}
int getAge() {
return age;
}
void displayinfo()
{
cout << "Name: " << name; cout << " Age: " << age << endl;
}
};
void swap(Person &p, Person &q)
{
Person temp;
temp.name = p.name;
temp.age = p.age;
p.name = q.name;
p.age = q.age;
q.name = temp.name;
q.age = temp.age;
}
int main()
{
int userValue;
Person po("Jessica", 24);
Person po2("Robert", 49);
Person po3("Maria", 47);
Person po4("John", 19);
Person family[4] = {po,po2,po3,po4};
int sort(family[4].getAge());
{
for(int i = 0; i < 3; i++)
if (family[i].getAge() < family[i+1].getAge())
{
swap(family[i], family[i+1]);
}
}
for(int i = 0; i < 4; i++)
family[i].displayinfo();
}
int sort(family[4].getAge()); is not a function declaration (and you can't implement a function inside of another function). It is actually a variable declaration. It is declaring a variable int sort that is initialized with the value from family[4].getAge() (which is undefined behavior since index 4 is out of bounds of your family[] array).
So, you are declaring an unused sort variable, and then you enter your for(int i = 0; i < 3; i++) loop, which DOES NOT perform a full sort of the array. For what you are attempting, use the standard std::sort() algorithm instead, eg:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class Person
{
public:
string name;
int age;
Person(string name = "empty", int age = 0)
{
setName(name);
setAge(age);
}
void setName(string x) {
name = x;
}
string getName() const {
return name;
}
void setAge(int y) {
age = y;
}
int getAge() const {
return age;
}
void displayinfo() const
{
cout << "Name: " << name; cout << " Age: " << age << endl;
}
};
int main()
{
Person po1("Jessica", 24);
Person po2("Robert", 49);
Person po3("Maria", 47);
Person po4("John", 19);
Person family[4] = {po1, po2, po3, po4};
std::sort(family, family + 4,
[](const Person &p1, const Person &p2) {
return p1.getAge() < p2.getAge();
}
);
for(int i = 0; i < 4; i++) {
family[i].displayinfo();
}
return 0;
}
Live Demo
Note that your family[] array holds copies of the Person objects that you initialize it with. To avoid the overhead of those copies, you can sort pointers instead, eg:
int main()
{
Person po1("Jessica", 24);
Person po2("Robert", 49);
Person po3("Maria", 47);
Person po4("John", 19);
Person* family[4] = {&po1, &po2, &po3, &po4};
std::sort(family, family + 4,
[](const Person *p1, const Person *p2) {
return p1->getAge() < p2->getAge();
}
);
for(int i = 0; i < 4; i++) {
family[i]->displayinfo();
}
return 0;
}
Live Demo
Or, you can get rid of the individual p0... objects altogether and initialize the array directly instead, eg:
int main()
{
Person family[4]{
{"Jessica", 24},
{"Robert", 49},
{"Maria", 47},
{"John", 19}
};
std::sort(family, family + 4,
[](const Person &p1, const Person &p2) {
return p1.getAge() < p2.getAge();
}
);
for(int i = 0; i < 4; i++) {
family[i].displayinfo();
}
return 0;
}
Live Demo

Sorting book title alphabetically

I use a for loop and if-else statement to try to sort the titles of the books alphabetically. However, I am facing some errors under the returnlistofBooks method. Is there any ways to fix this problem?
voidBookshelf::voidBookshelf(vector <Book*> listofBooks){
this->listofBooks = listofBooks;
}
void voidBookshelf::addBook()
{
int ID;
string Title;
string Author;
for (int i = 1; i <= 5; i++)
{
cout << "Book#"<< i << ":" << endl;
cout << "Enter an ID:";
cin >> ID;
cout << "Enter a title:";
cin >> Title;
cout << "Enter an author:";
cin >> Author;
Book *mybook = new Book(ID, Title, Author); //book object
listofBooks.push_back(mybook);
}
}
void returnListofBooks(int count, string name)
{
Book temp;
for (int i = 0; i < count; i++)
{
for (int j = 0; j < count - i; j++)
{
if (books[j].author > books[j + 1].author)
{
//swapping the instances themselves, but still comparing by the member.
temp = books[j];
books[j] = books[j + 1];
books[j + 1] = temp;
}
}
}
}
int main(){
voidBookshelf * myBookshelf = new voidBookshelf;
myBookshelf->addBook();
myBookshelf->returnListofBooks();
return 0;
}
you can check here for sort function!it is quite simple!
go to:
http://www.cplusplus.com/articles/NhA0RXSz/
An easy method to allow sorting is to overload operator< in your class:
class Book
{
public:
bool operator< (const Book& b)
{
return author < b.author;
}
private:
std::string author;
};
If you want to sort by other fields, you will need to write a custom comparison operator:
class Book
{
std::string title;
friend bool order_by_title(const Book& a, const Book& b);
};
bool order_by_title(const& Book a, const Book& b)
{
return a.title < b.title;
}
std::vector<Book> library;
//...
std::sort(library.begin(), library.end(), order_by_title);

How to use an accessor method that takes the user index and returns his/her first name.

3.) string getFirstName(int ind) const: An accessor method that takes the user index and returns his/her first name.
This is the problem im working on towards my program and as it says above im supposed to use an accessor method that takes the user index and returns his/ her first name???
Here is my code so far but im very confused how to go about using a accessor method
string RatingDB::getFirstName(int ind) const
{class getFirstName;
{
public:
return "";
}
}
For reference here is my program code so far
// TODO: implement all these method stubs!
void RatingDB::loadFile(string filename)
{
ifstream OpenFile;
OpenFile.open (filename);
// if this file does not exist then end the program
if (!OpenFile.is_open())
{
cout << "can't open this file!" << endl;
exit(EXIT_FAILURE);
}
int i = 0;
char c;
while(! OpenFile.eof())
{
do {
c=OpenFile.get();
if(c!= ' ')
m_firstNames[i].push_back(c);
} while(c!= ' ');
do {
c=OpenFile.get();
if(c!= ' ')
m_lastNames[i].push_back(c);
}while(c!= ' ');
do {
c=OpenFile.get();
if(c!= '/n' && OpenFile.eof())
m_Ratings[i].push_back(c);
} while(c!= '/n' && OpenFile.eof());
i++;
}
}
string RatingDB::getFirstName(int ind) const
{class getFirstName;
{
public:
return "";
}
}
string RatingDB::getLastName(int ind) const
{
return "";
}
vector<int> RatingDB::getRatingVector(int ind) const
{
return vector<int>();
}
int RatingDB::getNumberOfUsers() const
{
return -1;
}
void RatingDB::insertRating(string firstName, string lastName, string strRatings)
{
}
void RatingDB::displayRatings() const
{
}
int RatingDB::compareRatings(const vector<int>& RatingVector1, const vector<int>& RatingVector2) const
{
return -1;
}
void RatingDB::findSimUsers(const vector<int>& ratingVector, vector<int>& friendVector, vector<int>& scoreVector)
{
}
vector<int> RatingDB::parseVectorString(string strRatings)
{
return vector<int>(1);
}
//************************************************************************************************************************
void RatingDB::findTenBestMatches(const vector<int>& RatingVector, vector<int>& returnVector)
{
vector<int> sortVector(getNumberOfUsers()); //create a vector of integers representing each index in your vector of structures
bool swapped; //a boolean to indicate if any swaps have been made during this pass of the loop.
for(int i = 0; i < getNumberOfUsers(); i++) //initialize the array so that we have an integer indicating the location in
sortVector[i] = i; //your structure vector for every user that is read in.
do{ //keep doing the following if we swap at least once during the pass
swapped = false; //set the swapped variable for this pass
for(int i = 0; i < getNumberOfUsers() - 1; i++) //we are going to look at each element in the vector and see if it has a ranking lower
{ //than the next element. If so, the two need to be swapped.
if(compareRatings(RatingVector, getRatingVector(sortVector[i])) < compareRatings(RatingVector, getRatingVector(sortVector[i + 1])))
{ //we compare the two rankings and swap the two in the array if the first is lower than the second
//(we want the best rankings first)
int temp;
temp = sortVector[i]; //swap this one and the next one
sortVector[i] = sortVector[i + 1];
sortVector[i + 1] = temp;
swapped = true; //if we moved anything, then another pass is necessary
}
}
}while(swapped);
for(int i = 0; i < 10; i++) //copy the top ten indexes to the returnVector
if(i < getNumberOfUsers()) //if there are at least 10 users
returnVector.push_back(sortVector[i]); //copy the index
}
You want to change your perspective. Instead of having an array for first names and another for last names, you should put the first and last names into a structure and use a vector of that structure.
class Person
{
std::string m_first_name;
std::string m_last_name;
public:
Person(const std::string& first_name,
const std::string& last_name)
: m_first_name(first_name),
m_last_name(last_name)
{ }
};
typedef std::vector<Person> Person_Container;
int main(void)
{
Person jack("Jack", "Frost");
Person alton("Alton", "Brown");
Person bobby("Bobby", "Flay");
Person_Container people;
people.push_back(jack);
people.push_back(alton);
people.push_back(bobby);
return EXIT_SUCCESS;
}
Edit 1: Input A Person
You can augment the Person class by adding input methods.
class Person
{
std::string m_first_name;
std::string m_last_name;
public:
Person(const std::string& first_name = "",
const std::string& last_name = "")
: m_first_name(first_name),
m_last_name(last_name)
{ }
void Input_From_User(std::istream& inp,
std::ostream& out);
};
void
Person ::
Input_From_User(std::istream& inp, std::ostream& out)
{
out << "Input first name:\n";
std::getline(inp, first_name);
out << "\nInput last name:\n";
std::getline(inp, last_name);
}
int main(void)
{
Person somebody;
somebody.Input_From_User(cin, cout);
std::vector<Person> people;
people.push_back(somebody);
return EXIT_SUCCESS;
}
Likewise, you can augment the Person class for output as well.
That, is left as an exercise for the reader. :-)

Overloading operator not working in C++

Why is it that below code is always returning the last player in the array?
The operator bool operator > (Player &P) { return Age > P.Age; }seems not to be working properly. I see it as "compare the age of the element in the left object to the age of the element in the right object". Any suggestions?
Note: I don't intend to use std::vector or std::string.
#include <iostream>
using namespace std;
class Player {
int Age;
char *Name;
public:
Player() {}
void setName(char *n) { Name = n; }
void setAge(int a) { Age = a; }
bool operator > (Player &P) { return Age > P.Age; }
void showName(){ cout << Name; }
};
int main()
{
Player *ArrayPlayers = new Player[2];
ArrayPlayers[0].setName("Player1");
ArrayPlayers[0].setAge(27);
ArrayPlayers[1].setName("Player2");
ArrayPlayers[1].setAge(29);
ArrayPlayers[2].setName("Player3");
ArrayPlayers[2].setAge(24);
Player *Oldest = &ArrayPlayers[0];
for (int i = 0; i < 3; i++) {
Player *Current = &ArrayPlayers[i];
if (Current > Oldest) {
Oldest = Current;
}
}
cout << "Oldest player is: ";
Oldest->showName();
getchar();
}
It's because you are comparing pointers (Player*) not Player instances. The simplest fix to achieve the desired result is to dereference:
if (*Current > *Oldest) {
This will invoke your operator> implementation for the Player class.

Unable to access members of a class; SegFault

I have a program where I am setting up a closed hash table. In each Element of the Hash table, there is a Student class which holds varies members (name, id, year, etc.). I am simply trying to print out what has been added to my array, but I keep getting a SegFault, and I don't know why. It is only in my print function, though. I have copied the line of code to my other functions or put them in different classes, and they work there, but not when I try to print from my print function. I am at the end of my rope, trying to figure out why I can access the memory location of each member, but not it's actual value.
Here is my program:
main.cpp:
using namespace std;
#include <cstdlib>
#include "hash.h"
int main()
{
string temp1;
string temp2;
string temp3;
string temp4;
string temp5;
string temp6;
Hash h;
do{
cout << "set> ";
cin >> temp1;
//Checking for quit command.
if(temp1.compare("quit") == 0)
{
return 0;
}
//checking for add command.
else if(temp1.compare("add") == 0)
{
cin >> temp2;
cin >> temp3;
cin >> temp4;
cin >> temp5;
cin >> temp6;
Student *s1 = new Student(temp2, temp3, temp4, temp5, temp6);
Element e1(s1);
h.add(e1);
}
//checking for remove command.
else if(temp1.compare("remove") == 0)
{
int r;
cin >> r;
h.remove(r);
}
//checking for print command.
else if(temp1.compare("print") == 0)
{
h.print();
}
//Anything else must be an error.
else
{
cout << endl;
cout << "Error! "<< endl;
}
}while(temp1.compare("quit") != 0);
}
hash.h:
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;
// Student Class
class Student{
private:
string firstName;
string lastName;
string id;
string year;
string major;
public:
//Constructor
Student(string a, string b, string c, string d, string e);
friend class Element;
friend class Hash;
};
//Element class
class Element{
private:
Student *data;
public:
int getKey();
Student* getData();
void printStudent();
//Constructor
Element(Student *e)
{
data = e;
};
friend class Hash;
};
class Hash{
private:
Element **array;
public:
void add(Element);
void print();
void remove(int);
//Constructor
Hash()
{
array = new Element *[10];
};
friend class Student;
};
hash.cpp:
#include "hash.h"
//The Constructor for Student
Student::Student(string a, string b, string c, string d, string e)
{
firstName = a;
lastName = b;
id = c;
year = d;
major = e;
}
//getKey function for Element Class
int Element::getKey()
{
int key = atoi(getData()->id.c_str());
return key;
}
Student* Element::getData()
{
return data;
}
void Element::printStudent()
{
string c = data->firstName;
cout<< "(" << c << ")";
}
//The add command
void Hash::add(Element e1)
{
int x = e1.getKey()%10;
int i = 0;
if(array[x] == NULL || array[x]->getData() == NULL)
{
array[x] = &e1;
}
else
{while(array[x] != NULL || array[x]->getData() != NULL)
{
x=(x+(i*i))%10;
if(array[x] == NULL || array[x]->getData() == NULL)
{
array[x] = &e1;
break;
}
else
{
i++;
}
}}
}
//The remove command
void Hash::remove(int n)
{
Element e2(NULL);
for(int j = 0; j<10; j++)
{
if(n == array[j]->getKey())
{
array[j] = &e2;
cout << "true" << endl;
break;
}
}
cout << "false" << endl;
}
//The Print command
void Hash::print()
{ int k = 0;
while(k<10)
{
if(array[k] == NULL)
{
cout << "(NULL)";
}
else if(array[k]->getData() == NULL)
{
cout << "(DEL)";
}
else
{
cout << "(" << array[k]->getData()->firstName << ")";
}
k++;
}
cout << endl;
}
Thank you for your help.
You have dangling pointers.
This function gets a temporary copy of an Element, calling it e1.
//The add command
void Hash::add(Element e1)
{
It then stores the address of this local variable.
array[x] = &e1;
And when Hash::add leaves scope, e1 no longer exists.
}
array[x] now points to memory that is no longer Element e1.
The general problem you are facing is that you have designed a Hash class that maintains pointers to objects, but has little control or knowledge regarding when those objects get destroyed.
You will need to personally ensure that objects added to your Hash last at least as long as the Hash does.
Simplest solution for your problem could be to store Element instances in Hash by value not by pointers. So:
class Hash{
private:
Element *array;
public:
void add(Element);
void print();
void remove(int);
//Constructor
Hash()
{
array = new Element[10];
};
friend class Student;
};
Now when you store new element or remove existing you copy them:
array[x] = e1; // not &e1 anymore
This is not very good practice, but at least could change your program in some workable state with minimal changes.