Trying to use a member function on a class array - c++

Using the code below, I want to use my sort function to take an Student array and sort them based on their gpa component. I have to use the parameters of a Student array and the size of the array. If you look towards the bottom of my int main function I try to call on the member sort to sort the array a but with no avail. The error I get is:
member reference base type Student [200] is not a structure or union.
How do I wrote my code to take array a and say to use the member Sort on it given the parameters I have to use. Thanks in advance. If this too much please let me know I'll try to specify even more.
class Student
{
private:
string ID, fname, lname, level;
double gpa;
public:
Student();
Student(string id, string first, string last, double Gpa, string grade);
string getID() const;
string getfirst() const;
string getlast() const;
string getlevel() const;
double getGPA() const;
void setID(string id);
void setfirst(string f1);
void setlast(string l1);
void setlevel(string lev);
void setGPA(double GPA1);
friend void Sort(Student studentlist[], int size);
friend ostream& operator <<(ostream& ost, Student S1);
};
int main()
{
ifstream ins;
ofstream outs;
ins.open("input.dat");
outs.open("output.dat");
if(ins.fail())
{
cout << "File not found.";
exit(1);
}
if(outs.fail())
{
cout << "Output file not opened.";
exit(1);
}
Student a[200];
int x = 0;
while(!ins.eof())
{
string id, fnam, lnam, levl;
double point;
ins >> id >> fnam >> lnam >> point >> levl;
a[x].setID(id);
a[x].setfirst(fnam);
a[x].setlast(lnam);
a[x].setGPA(point);
a[x].setlevel(levl);
if(a[x].getID() == "")
{
break;
}
x += 1;
}
if(x == 0)
{
cout << "File is empty" << endl;
return 0;
}
x = x +1;
a.Sort(a, x);
int t=0;
while(t<x)
{
outs << a[t];
t += 1;
}
outs.close();
ins.close();
return 0;
}

Get rid of the a.. Since Sort is a free function, you need just
Sort(a, x);

In C++, arrays are not class objects, so there is no Sort method like there is in C#, however you can use std::sort:
using namespace std;
Student array[200];
// (populate `array` here)
sort(
begin(array),
end(array),
[](const Student& x, const Student& y) -> bool {
return x.gpa > y.gpa;
}
);
I recommend using std::Array<T> instead of "raw" arrays for greater runtime safety and to avoid needing to keep track of the array length separately:
I note that you're storing Student objects as values, not pointers, so "moving" a Student to another index in the array will be expensive because it will copy the entire object. Consider allocating Students separately and only sorting an array of Student* pointers instead.

Use of
a.Sort(a, x);
is incorrect on couple of accounts.
a is an array type, specifically of type Student [200]. Arrays don't have member functions. Hence, use of a. is not allowed.
Sort is a non-member function. Hence it cannot be called with the .Sort() syntax.
Just use:
Sort(a, x);

Related

Implement C++ sort function, according to the input items to sort

I create an array of a struct:
struct student {
char name[20];
int num1;
int num2;
} temp[20] = {0};
Its internal data is like this:
temp[0]={'a',1,11};
temp[1]={'b',2,12};
temp[2]={'c',3,13};
temp[3]={'d',4,14};
temp[4]={'e',5,15};
...
I know that I can define a compare function to tell the sort() function the role of sorting, for example:
bool cmp (student a,student b){
return a.num1 > b.num1;
}
sort(temp, temp+20, cmp);
My question is: How could I sort the array using this sort() function based on the items I read in with scanf()?
Specifically, if I scanf() the num1 field, the sort function sorts the data based on num1.
If I scanf() the num2 field, the sort function sorts the data based on num2.
The role of sorting follows my scanf() item.
So, how can I realize this?
You can have both the reading and the sorting depend on a pointer-to-member.
So instead of
void read_num1(student & s) {
std::scanf("%d", std::addressof(s.num1));
}
void compare_num1(student lhs, student rhs) {
return lhs.num1 < rhs.num1;
}
int main() {
student students[20];
for (student & s : students) {
read_num1(s);
}
std::sort(std::begin(students), std::end(students), compare_num1);
}
You have functions that have an extra parameter, and close over that where necessary
using member_t = int (student::*);
void read(student & s, member_t member) {
std::scanf("%d", std::addressof(s.*member));
}
void compare(student lhs, student rhs, member_t member) {
return (lhs.*member) < (rhs.*member);
}
int main() {
member_t member = /* some condition */ true ? &student::num1 : &student::num2;
student students[20];
for (student & s : students) {
read(s, member);
}
std::sort(std::begin(students), std::end(students), [member](student lhs, student rhs) { return compare(lhs, rhs, member); });
}
I recommend using the ranges interface for sort in the STL. Change your array to use std::array or better std::vector and then you can do sorting as simple as this:
ranges::sort(s, [](int a, int b) { return a > b; });
print("Sort using a lambda expression", s);
Particle particles[] {
{"Electron", 0.511}, {"Muon", 105.66}, {"Tau", 1776.86},
{"Positron", 0.511}, {"Proton", 938.27}, {"Neutron", 939.57},
};
ranges::sort(particles, {}, &Particle::name);
print("\nSort by name using a projection", particles, '\n');
ranges::sort(particles, {}, &Particle::mass);
print("Sort by mass using a projection", particles, '\n');
And if you want to sort by multiple fields, e.g last_name, first_name, you can use a std::tuple with references into your struct as projection. std::tuple has lexicographical comparison so you really just have to give the order of fields and the rest happens by magic.

Is there a way to pass user data for a setter without using an extra variable in C++?

I'm still new to C++ and so far I used to pass static values to setter methods. Now I'm trying to pass user data to the methods, but so far I can only do this using an extra variable as follows.
Class:
class Square
{
private:
double length;
public:
void setLength(double l);
double getlength();
double calcArea();
};
In main function:
Square s1;
double x;
cout << "Enter length: ";
cin >> x;
s1.setLength(x);
Thus, I use a temporary variable to pass user values to setters.
My question is, is there a way to directly pass variables to the setter? or is there a better way?
Please note that I am a beginner in C++, therefor please be descriptive and simple. Thanks!!
I don't think it's a good idea but this works :
#include <iostream>
using std::cout;
using std::cin;
class Square
{
private:
double len;
public:
double& length() {return len;}
double length() const {return len;}
};
int main() {
Square s1;
cout << "Enter length: ";
cin >> s1.length();
cout << "length: " << s1.length();
}
you can do like this
#include<iostream>
class Square
{
private:
double length;
friend std::istream& operator>>(std::istream&,Square& );
public:
void setLength(double l);
double getlength();
double calcArea();
};
std::istream& operator>>(std::istream& is, Square& s )
{
is>>s.length;
return is;
}
int main()
{
Square s;
std::cout<<"Enter length: ";
std::cin>>s;
}
It should be a design question. Does being able to load directly a specific attribute from a stream make sense? If it has a semantic value (it makes sense at the business object level) or helps to follow the DRY (do not repeat yourself) princip by avoiding code duplication, then you should write a specific method:
class Square {
private:
double length;
...
public:
std::istream& loadlength(istream& in) {
in >> length;
return in;
}
...
You can then use it easily:
s1.loadlength(std::cin);
if (! std::cin) {
// process the error condition
....
You can either set the value using a variable or you can send a value, but the thing is that if you want to do it directly, it will break the law of Encapsulation. We can the class attributes private to make sure of Encapsulation. So for that, the thing you did is perfect ✌🏻.

Two types of value in one vector C++

Say I have a vector for class Students, but the requirement of the question states that I cannot put grade together in the constructor, so I will have to work around it.
Is it possible that I have two values in one vector slot?
For example, the class Student and string grade are my arguments.
vector<Student*, string> Students;
So, at the end of the day, if I cout the vector, I should get Student AND the grade in one slot.
Does this work? If so, how do I push_back the value?
Otherwise, is there another way to do this?
std::vector<std::pair<Student*, string>> students;
or even better:
std::map<Student*, string> students;
Pushing values:
first case:
students.push_back(std::make_pair(x,y));
even better(as #snps advised):
students.emplace_back(x, y);
second case:
students[x]=y;
Be aware that in the second case you can not add multiple record with the same Student* value. If you try it will write over the old one.
There are multiple ways of achieving this.
A. Array of structures:
A1. Composition:
class GradedStudent {
Student body;
string grade;
public:
GradedStudent(const string& grade);
// reimplement all methods of `Student`,
// delegating implementations to `body`
};
std::vector<GradedStudent> gradedStudents;
A2. Inheritance (all students have grades):
class Student {
public:
Student();
};
class GradedStudent {
string grade;
public:
GradedStudent(const string& grade);
};
std::vector<GradedStudent*> gradedStudents;
A3. Inheritance + polymorphysm
(some students have grades and some don`t):
class Student {
public:
Student();
virtual ~Student();
virtual const string& getGrade() const { return ""; }
};
class GradedStudent : public IStudent {
string grade;
public:
GradedStudent(const string& grade);
virtual string getGrade() const override { return grade; }
};
std::vector<Student*> students;
// non graded students will have empty string as a grade
B. Structure of Arrays approach (separate array for grades):
std::vector<Student*> students;
std::vector<string> grades;
for(int i = 0; i < students.size(); ++i) {
auto* student = students[i];
auto grade = grades[i];
}
C. Associative container (doesn`t honor "use std::vector" requirement):
std::map<Student*, string> students;
for(auto student : students) {
auto* student = student.first;
auto grade = student.second;
}
Those are classic ones. There might be more.
If all students have grades, you should really make grade a member of this class.
P.S. Do you really need to store pointers in a vector (as opposed to storing objects)?
You can use std::map for this.
http://www.cplusplus.com/reference/map/map/

c++ char array output in class functions

I am a real c++ beginner and I have a problem with my char array output in a c++ excerise. I was asked to transform a certain UML class in to c++ and generate an working output with the parameters given in main. Here ist the code:
#include <iostream>
#include <stdlib.h>
/*My class defintion book*/
class Book
{ protected:
long int number;
char author[25];
int year;
bool lent;
void setLent(bool x);
bool getLent();
public:
Book(long int n, char a[25], int j, bool x);
long int getNr();
int getYear();
void print();
};
/*Method definition Book*/
Book::Book(long int n, char a[25], int j, bool x)
{number=n;
author=a;
year=j;
lent=x;}
long int Book::getNr()
{return number; }
int Book::getYear()
{return year;}
void Book::setLent(bool x)
{lent=x;}
bool Book::getLent()
{return lent;}
void Book::print()
{
std::cout << "Book Nr: " << number << std::endl;
std::cout << "Author: " << author << std::endl;
std::cout << "Year: " << year << std::endl;
if (lent==0)
std::cout << "Lent [yes/no]: no" << std::endl;
else
std::cout << "Lent [yes/no]: yes" << std::endl;
}
/*MAIN*/
int main()
{
Book b1(123456, "test", 2014, false);
b1.print();
system("pause");
return 0;
This is my output:
Book Nr: 123456
Author: b<Vv-[[vóYA
Year: 2014
Lent [yes/no]: no
Press any key to continue...
As you can see all outputs work except for the "Author". There I am getting crap. Note that I have to use char as type. since it is given in the UML class I had to transform into c++.
I really searched everywhere. But didn't find the correct solution. I have the feeling it will be a very simple one...
Thanks in advance for your help!
The reason this doesn't work is that you're assigning your pointer author to another pointer a, which then goes out of scope... so you're left with author pointing to some garbage. If you want to stick with character arrays, you'll have to copy all the data that a points to:
strcpy(author, a);
But since it's C++, you should just use strings, which are easier to deal with:
class Book {
...
std::string author;
....
};
Book::Book(long int n, const std::string& a, int j, bool x)
: author(a), ...
{ }
You are printing out uninitialized data.
Make author a string
#include <string>
class Book
{ protected:
long int number;
std::string author;
int year;
bool lent;
and make the argument to the constructor a string as well
Book::Book(long int n, const std::string& a, int j, bool x)
Arrays of characters are not as flexible as std::strings. they are just chunks of data. If you want to use strings then use std::string instead.
Also, use an initializer list in C++ constructors, not java style
Book::Book(long int n, const std::string &a, int j, bool x)
: number(n),
author(a),
year(j),
lent(x)
{ }
There are two bugs in your code:
Book::Book(long int n, const char a[25], int j, bool x)
{
number=n;
strncpy(author, a, 25); // author = a; doesn't work! shouldn't compile either...
year=j;
lent=x;
}
First: The variable author is a pointer to a zero terminated string. You can use strcpy() to copy this string. Therefore you need to #include <memory.h. But you need to be sure that the string -is- really zero-terminated and fits into your target variable! Else you'll overwrite other memory regions next to the target variable, which is also called a buffer overflow! Better use strncpy(target, source, maxlength); which avoids this problem.
Second: Your parameter a should be "const" as you want to be able to call it with a string constant like in Book b1(123456, "test", 2014, false); where "test" is a constant!
As others already suggested you should use std::string instead of a[25]. C-Strings are "C" and not "C++" and you should try to avoid them. C-Strings can introduce a lot of bugs into your code and enable buffer overflows (=security problems), too. Also they are more complicated to handle. You need to #include <string> to use them.

How to name comparison predicate function to sort vector

I'm a beginner at C++ and have a question regarding sorting a vector of a class object. I've done a lot of research online but can't seem to figure out the answer. My problem is that I want to have multiple predicate comparison functions that I can name, as I need to have the user be able to choose from a menu which private member variable/component of the vector to sort on. I'm able to get a predicate comparison function with an overloaded operator to work ok, but when I try and name the function, I can't seem to get it to compile without a bunch of errors (which I'll post farther down underneath the appropriate code).
When the code's run, a vector of a BookData object is created by a function that reads a text file. Function prototype in main:
vector<BookData> createVector();
Function call in main:
vector<BookData> books = createVector();
createVector function definition:
vector<BookData> createVector()
{
const int LENGTH = 81;
char input[LENGTH];
vector<BookData> fBooks;
string vBookTitle, vIsbn, vAuthor, vPublisher, vDateAdded;
int vQtyOnHand;
double vWholesale, vRetail;
BookData *books1;
books1 = new BookData;
fstream dataFile("Serendipity.data");
if (dataFile.is_open())
{
while (!dataFile.eof())
{
dataFile.getline(input, LENGTH, '\t');
vBookTitle = input;
books1->setTitle(vBookTitle);
dataFile.getline(input, LENGTH, '\t');
vAuthor = input;
books1->setAuthor(vAuthor);
dataFile.getline(input, LENGTH, '\t');
vPublisher = input;
books1->setPub(vPublisher);
dataFile.getline(input, LENGTH, '\t');
vIsbn = input;
books1->setIsbn(vIsbn);
dataFile >> vQtyOnHand;
books1->setQty(vQtyOnHand);
dataFile >> vWholesale;
books1->setWholesale(vWholesale);
dataFile >> vRetail;
books1->setRetail(vRetail);
dataFile.ignore();
dataFile.getline(input, LENGTH);
vDateAdded = input;
books1->setDateAdded(vDateAdded);
fBooks.push_back(*books1);
}
}
return fBooks;
}
This is my class definition file (bookdata.h) with the working comparison function:
class BookData
{
private:
string bookTitle, isbn, author,
publisher, dateAdded;
int qtyOnHand;
double wholesale, retail;
bool empty;
public:
BookData();
bool operator< (BookData rhs);
void printVector();
void setTitle(string);
void setIsbn(string);
void setAuthor(string);
void setPub(string);
void setDateAdded(string);
void setQty(int);
void setWholesale(double);
void setRetail(double);
int isEmpty();
void insertBook();
void removeBook();
string getTitle();
string getIsbn();
string getAuthor();
string getPublisher();
string getDateAdded();
int getQtyOnHand();
double getWholesale();
double getRetail();
};
And this is the working function in the class implementation file (bookdata.cpp):
bool BookData::operator< (BookData rhs)
{
return qtyOnHand < rhs.qtyOnHand;
}
And it's called like so from int main():
sort (books.begin(), books.end());
However, if I try and name the function:
bool compare (BookData rhs);
from within the class definition file and change it to this in the class implementation file:
bool BookData::compare (BookData rhs)
{
return qtyOnHand < rhs.qtyOnHand;
}
and change the vector sort function so within main:
sort (books.begin(), books.end(), &BookData::compare);
I get an error message from the compiler:
error C2064: term does not evaluate to a function taking 2 arguments
Any suggestions as to how I can properly name/call the sort function?
The Compare shouldn't be a member of the class (or be a static member or friend if it accesses restricted fields), and it should accept both the values to compare (whereas operator < uses this as the left value).
something like this:
bool compare (BookData lhs, BookData rhs)
{
return lhs.qtyOnHand < rhs.qtyOnHand;
}
Using operator< is IMHO a better approach.
I recommend to create a predicate as function object:
struct compare
{
bool operator()(const BookData& x, const BookData& y) const
{
return x < y;
}
};
Note the const at the end of operator(). You can pass this object to STL algorithms as follows:
sort (books.begin(), books.end(), compare());
If you do not want to create the function object each time, you may include it as (static) member in BookData.
BTW, suggest to pass-by-reference instead of pass-by-value for the parameters, like this:
bool compare (const BookData &lhs, const BookData &rhs)
{
// implementation.
}