Help with Friends and Overloading operators

NOTE: My understanding of Programming is very low, the class design at my school is very flawed. Please excuse my lack of knowledge.

In my programming class, I'm supposed to design a program that creates a database of student records using a linked list. I have my 2 classes, one for the node class(Record) and the other for the list class(Database). Basically, I got my insertRecord, deleteRecord, and findRecord functions working fine, the problem comes with printing the entire database. The teacher wants the << operator overloaded used to print the entire database. Such as:

1
2
Database PM1;
Cout<<PM1 ;


To output:


ID    First_Name    Last_Name   Age    Major    Grade
12345 John          Smith       20     ECE      A
12347 Alice         Bob         19     Biology  B
13456 Cathy         Morgan      21     CS       C



This is my Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ostream &operator<<(ostream& os, Database &d)
{
os << setw(10) << "STUDENT ID" << setw(10) << "FIRST NAME" << setw(10)
<< "LAST NAME" << setw(10) << "AGE" << setw(10) << "MAJOR" << setw(10) << 
"GRADE" << endl;

Record* tp=d.root;

while(tp !=0)
{
os << setw(10) << tp->student_ID << setw(10) << tp->first_name    << setw(10)
<< tp->last_name << setw(10) << tp->age << setw(10) << tp->major << setw(10) 
<< tp->grade << endl;
tp=tp->next;
}
return os;
}


But I'm obviously doing something wrong. I can't access the data in the actual node of the database. I'm not really sure what exactly I'm doing. I can add more code if necessary.
Last edited on
Why you cannot access data in a Record? How do you set that data?
Is "root" a private member of your "Database" class ? If that's the case you need to make this operator<< a fiend function of the class.
can you link the other code?
"root" is a private member of the Database class. I have the constructor set to the null pointer when it gets called with a member initializer list. Then as I insert records using the InsertRecord function, the root pointer points to the first record in the list. The error comes when I use the tp->student_ID. The student_ID, first_name and so on are members of the Record class but Database is a friend of the Record class. Can it not access those members in that case?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef DATABASE_H
#define DATABASE_H

#include "Record.h"

class Database
{
    public:
        Database();
	friend ostream &operator<<(std::ostream&, Database&);
        void InsertRecord(int&, string&, string&, int&, string&, char&);
        void DeleteRecord(int);
        void FindRecord(int);
    private:
        Record* root; // starting record;
};

#endif // DATABASE_H 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef RECORD_H
#define RECORD_H

#include <iostream>
#include <string>

using namespace std;

class Record
{
	friend class Database;
    public:
        Record(int, string, string, int, string, int);
        friend ostream &operator<<(std::ostream&, const Record&);
    private:
        Record* next;
        int student_ID;
        string first_name;
        string last_name;
        int age;
        string major;
        char grade;
};

#endif // RECORD_H 


I also realized that I overloaded << twice to do 2 separate things, one for when you << a database class and one for when you << a record class. I don't that's the issue, but maybe possibly it is? I'm pretty sure it has something to do with the way I friended or overloaded the functions and/or classes. If I did, can someone explain why it's wrong?
The friend function to your record class is ostream &operator<<(std::ostream&, const Record&), yet the one that tries to access its members is ostream &operator<<(ostream& os, Database &d)

Try changing your code to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ostream &operator<<(ostream& os, const Record& r)
{
    os << setw(10) << r.student_ID << setw(10) << r.first_name    << setw(10)
    << r.last_name << setw(10) << r.age << setw(10) << r.major << setw(10) 
    << r.grade << endl;
    return os;
}

ostream &operator<<(ostream& os, Database &d)
{
    os << setw(10) << "STUDENT ID" << setw(10) << "FIRST NAME" << setw(10)
    << "LAST NAME" << setw(10) << "AGE" << setw(10) << "MAJOR" << setw(10) << 
    "GRADE" << endl;

    Record* tp=d.root;

    while(tp !=0)
    {
        os << *tp; // here we use ostream &operator<<(ostream& os, const Record& r)
        tp=tp->next;
    }
    return os;
}
Last edited on
Thats what I had first, but the thing is the Record << operator has to do something else, for a different part of the project:
1
2
Record record1; // an object of class Record
Cout<<record1; // should display the information of the student like below: 


outputs:

Student_ID : 12345
First_Name : John
Last_Name : Smith
Age : 20
Major: ECE
Grade: A


Also can someone explain to me why some operator needs the friend keyword and others don't?
Last edited on
Then you need to declare ostream &operator<<(ostream& os, Database &d) as a friend of your Record class or provide public methods that return the value of the attributes.

Also can someone explain to me why some operator needs the friend keyword and others don't?

Because it's not an operator of the class but an overload of ostream::operator<<().
hey i am working on something very similar, would you mind linking the source code file. I am not great with classes and could use some help.
@tuom
Thanks a lot, I guess my question is how can you access the members of the Record class through the Database class, which points to type Records? Because my next step is to overload the equality operator to return whether 1 database object is equal to another database object, which I'm not sure how to do if you can't access each piece of data. My thought process was to traverse through both database objects and comparing the first database Student_ID, First_Name, Last_Name... to the second database's data, then pointing to the traversing pointer to next. That doesn't work if you can't access the Student_ID and other stuff. Is there a better way of doing it? Or is this the right way but I'm missing something?

@yellowbandit
I'm not really comfortable with giving you my work in the small chance that you go to my school, and we end up with very similar work so that we both fail. I can link you to the youtube video where I got most of the concepts.
http://www.youtube.com/playlist?list=PL3345A50B73C26A6F from videos 43-49 of the playlist, it explains the concepts and shows you how to use them.
Wait, I can do the same thing, making the == operator a friend to the Record class?
understandable, thanks for the link
I guess my question is how can you access the members of the Record class through the Database class, which points to type Records?

You could make the Database class a friend class of your Record Class or, better, add public methods to the Record class that let you read these values.

Because my next step is to overload the equality operator to return whether 1 database object is equal to another database object, which I'm not sure how to do if you can't access each piece of data.

You should overload this operator for both the Record class and the Database class and use the former in the latter.
Directly using the details of the implementation of a Record in Database::operator==() would break encapsulation.
supersteez wrote:
Wait, I can do the same thing, making the == operator a friend to the Record class?

Short: Yes.

Long: Depends on whose operator it is.

Standalone bool operator== ( const Record &, const Record & ) would have to be a friend, unless you add public methods to the Record that return values, e.g. int Record::getAge() const { return age; }.

Member bool Record::operator== ( const Record & ) const; obviously has access.

I see that Database is a friend of Record. Therefore, bool Database::operator== ( const Database & ) const; already has access.
Ok, thank you everyone for all the help. The last part of the project is to make a derived class from Record called Grade , which will ask user for Grades in 5 assignments, 2 quiz, 2 homeworks and 1 projects and
calculates the grade according to the following criteria:
Total = 40% Assigments + 10% Quiz + 25% Homeworks + 25% Project = 100%
Total > 90% ; Grades = A;
Total >80 && Total < 90; Grade = B
Total >70 && Total < 80 ; Grade = C;
Total > 60 && Total < 70; Grade = D;
Total > 50 && Total < 60; Grade = E;
Total < 50; Grade = F;
The variable ‘grade’ in base class should be set based on this total score.

I don't understand why and how I would make this a derived class. For some reason I can't wrap my head around it.
EDIT: I know that an object of a derived class calls the base class constructor when created, and since I only have a constructor in my Record member functions, that's really the only thing that gets inherited. So I'm really confused on why I would make this a separate class.
Last edited on
See it as a Record that also contains the grades for the assignment, quiz, homeworks and project and has methods to ask for these grades and compute the overall grade from them.
I'm sorry but I don't really understand how to do what you told me to do on the last part. How does that tie in to my Database?
I think you're supposed to use Grade objects instead of Record objects into your database.
lol I was thinking the same. The assignment says at the beginning to make each record of Database an object of class Record. But then it says to make this. Like I said in the first post, the class design is flawed. Including the instruction to projects that account to 40% of the grade. Surprisingly(sarcasm), the professors and both TAs are not answering any of the emails I sent about this project. I think I will just make the class Grade, and not implement it into my Database.
Topic archived. No new replies allowed.