Trying to build a database using classes.
This is just an excerpt of the classes, my main() creates a bunch of students using the class Student. Each student then has an ID and Name that are inputted later. Additionally, each student will have an array of 2 slots which will hold info for their courses they're taking. Those courses are created using the class Course.
What I'm trying to figure out is how can I place the course info (courseID and courseName) into a slot of the student's courses array once I assign them a course (in other words, student A is now in class 1. I want the courseID and courseName of class 1 to be assigned to student A's courses).
I try to use the locations of each course created in the main but that proves difficult trying to output. Is it possible to be in the class Student and have it call a function from class Course? Any help be great. Thanks.
class Course {
protected:
int courseID;
char* courseName;
public:
Course() {
courseID = 0;
courseName = "";
}
void makeID(int id, char* name) {
courseID = id;
courseName = name;
}
int getID() {
return courseID;
}
char* getCourseName() {
return courseName;
}
};
class Student : public Course {
private:
int studentID;
char* studentName;
int classCount;
int courses[2]; //could I change to: Course courses[2]?
char name[30];
int id;
public:
Student() {
studentID = 0;
studentName[30];
classCount = 0;
courses[2];
}
void makeStudent() {
cout << "Input 9 digit student ID: ";
cin >> id;
studentID = id;
cin.ignore();
cout << "Input first and last name of the student: ";
cin.getline(name, 30, '\n');
studentName = name;
return;
}
int getstudentID() {
return studentID;
}
int getclassCount() {
return classCount;
}
char* getstudentName() {
return studentName;
}
void addClass(int course) {
if (classCount == 2) {
cout << "Max amount of courses reached." << endl;
}
else {
courses[classCount] = course;
classCount++;
}
return;
}
int returnClass(int course) { //can't figure out what to do
return courses[course]; //here.
}
};
At very first, you should get a clear image how your data model shall look like.
OK, we have bunch of courses and a bunch of students. Courses and students are totally unrelated (apart from students attending courses) concepts at first, so it won't make any sense one inheriting from the other...
Imagining the scenario does not only cover one single period (school year, semester, trimester, ...), courses might change over time as well as will the students.
So you might have a vector or another data structure storing courses on one hand and students on the other.
Students will attend courses, this can be reflected in two ways:
courses holding lists of all students attending them
students holding lists of all courses they attend
Depending on use case, it might be appropriate to implement both redundantly.
How would you install such a relation ship? Easiest (at a first glance, at least) would be X holding one or more pointers to Y in both scenarios.
class Course
{
std::vector<Student*> participants;
public:
// whatever you need...
};
class Student
{
std::vector<Course*> students;
public:
// whatever you need...
};
As I now use pointers to represent the relation ship, the data structures holding all courses and students must not invalidate these if adding or removing students from! std::vector<X> would do so, so you cannot use it. Alternatives would be std::vector<std::unique_ptr<X>> or std::list<X>. Actually, std::vector<X*> would work as well, but then it would be you who would need to care for deletion, if removing courses or students, so prefer one of the other solutions.
But why did I not use smart pointers for the courses' and students' vectors? We'd be forced to use std::shared_ptr then for, but we won't profit from: If a student leaves our institution, we'd have to eliminate her/him anyway and remove her/him from all courses being attended. So we don't profit from smart pointers in this scenario (can be totally different in other ones). Analogously if a course is cancelled.
So make sure in the destructors the classes that
a course is removed from all students' vectors in its own vector
student is unregistered from all courses in its vector
With these basics, you can get ID, name and other data for a specific course a student attends simply via the pointer in the vector.
If you often seek via a specific information, (e. g. by name), you might have a std::map as well, facilitating the lookup by the specific attribute. You might use such a map for the complete list of courses/students as well, then, though, be aware that pointers are only guaranteed to remain valid in std::map<X, Y>, not in std::unordered_map<X, Y>, so if you intend to use the latter, you'd have to go the way via smart pointer again as with the vector: std::unordered_map<X, std::unique_ptr<Y>>.
Related
This is my first question on here, so excuse me if I've formatted everything in a wrong way.
So, to get to the problem - this is s university assignment of mine. The goal is to create a class called Student, which has a few fields, and store the instances in an array of Student objects. One of the tasks is to have a static variable inside the class that keeps track of how many Student instances have been created. To clarify, I have a getData() method that asks the user for the values, and then sets the current object's values to those entered (basically just like a constructor, which makes the constructors obsolete, but they want us to do it that way for some reason). In the getData() function, the static count variable gets raised by 1, as well as in the constructors, and gets decremented in the destructor.
The issue is that for some reason, Student::amount variable sets itself to 100. Every time that i try to access it from the main() function, its 100 plus the amount of students created, so if we have created 2 students, Student::amount will be equal to 102. I've nowhere explicitly set it to that number, which also matches the size of the array, by the way.
I'm still rather new to C++, I've read and watched some material on how OOP works here, but I've barely even scratched the surface.
Sorry again if my question is badly formulated or/and badly formatted, and I hope you can help me with this!
#include <iostream>
#include <string>
using namespace std;
class Date {
...
};
class Student {
// Fields
private:
string name = "";
string PID = "";
int marks[5]{ 0 };
short course = 0;
public:
// Count of all students
static int amount;
// Getters
...
// Setters
...
// Constructors
Student(); // Default one, Student::amount gets raised by 1 there
Student(string name, string PID, int marks[5], short course);
// Destructor
~Student();
// Methods
void getData();
void display(); // Display the information of a student
};
// Array of students
Student students[100];
// Student::Count
int Student::amount; // Initializes with 0 if not explicitly initialized
void Student::getData() {
cin.ignore();
cout << "\nName: "; getline(cin, name);
cout << "PID: "; getline(cin, PID);
cout << "Marks:\n";
for (int i = 0; i < 5; i++)
{
cin >> marks[i];
}
cout << "Course: "; cin >> course;
Student::amount++;
}
Student::Student(string name, string PID, int marks[5], short course) {
this->setName(name);
this->setPID(PID);
this->setMarks(marks);
this->setCourse(course);
Student::amount++;
}
The global declaration Student students[100]; calls the default Student constructor 100 times, before main is reached. According to your comment (you don't supply the constructor implementation), that constructor increases amount by 1.
A solution here is to remove Student::amount and instead use
std::vector<Student> students;
students.size() will give you the number of students in that container. Use push_back to put students into the vector.
A very crude alternative that at least is broadly compliant with the question constraints is to remove the amount increment from the default constructor, and all other places apart from the four argument constructor.
Apologies in advance if this is below the community's paygrade. I just started learning about OOP and English is not my native language.
I just learned about using getters and setters, and the importance of data encapsulation. However, All the examples I've seen online using getters and setters all deal with static values such as the one below:
#include <iostream>
using namespace std;
class Employee {
private:
// Private attribute
int salary;
public:
// Setter
void setSalary(int s) {
salary = s;
}
// Getter
int getSalary() {
return salary;
}
};
int main() {
Employee myObj;
myObj.setSalary(50000);
cout << myObj.getSalary();
return 0;
}
I've managed to allow user input by declaring another set of variables and feeding them as parameters into the setters like below:
int main() {
Employee myObj;
int salary;
cout<<"Enter salary: " << endl;
cin>>salary;
myObj.setSalary(salary);
cout << myObj.getSalary();
return 0;
}
Is this the only way to allow user input? Is there a more elegant way of accomplishing this other than declaring another set of local variables?
Sorry if this is a beginner question. I'm new to the C++ game and am still getting my feet wet.
Is this the only way to allow user input? Is there a more elegant way of accomplishing this other than declaring another set of local variables?
(and from comment)
I was hoping I could do some variation of myObj.setSalary(cin) so cin would feed the value directly into the getter without me having to pass it into temp variables
I would not mix input from stdin with setters, better keep them as seperate methods:
class foo {
int x;
int other_member;
public:
void set_x(int a) { x = a; }
void set_other_member(int a) { other_member = a; }
void read_x(std::istream& in) {
in >> x;
}
};
This lets you write
f.read_x(std::cin);
to read x from stdin.
Note that there are many ways to accomplish the same and what is "elegant" is rather subjective. Typically you would provide an overload for operator>> to read a foo from a stream:
std::istream& operator>>(std::istream& in, foo& f) {
int a,b;
in >> a >> b;
f.set_x(a);
f.set_other_member(b);
}
Either this (using the setters) or you make the operator a friend of foo (to access privates directly) or you use foo::read_x to implement it. Then you can write
foo f;
std::cin >> f;
Note that C++ is not a "pure" OOP language, and idiomatic C++ will happily eschew certain OOP principles in favor of simpler and more effective code where applicable.
It is perfectly acceptable, if you want a datatype to just "hold" data, to use a plain old struct (with public data fields). Don't confuse the benefits of encapsulation (...of non-trivial constructs, so you can change their implementation internally) with any kind of "security". Getters/Setters or not, anyone can change your object's data if he wants to. Incidentially, this makes reading data easier.
cin >> e.salary;
If you don't want "just a struct", it is good style to have a constructor that sets meaningful initial values, so that you don't have "uninitialized" Employee objects flying around (RAII). This means that neither your client nor your destructor has to deal with the possibility that Employee might not actually be one (hold null pointers, or other kinds of uninitialized resources).
Employee e( "Mark Smith", 10000 );
Once you are at that point, Employee should be responsible for anything that happens to / with an Employee object. That implies that it should not be somebody else that's reading user input and writes it to Employee data fields (via setters or not), it should be a message sent to the Employee object that it should update its fields, from input or otherwise, as appropriate.
(Consider the possibility of someone setting a pointer member of yours to nullptr, or worse, some random value. Your destructor would throw a fit trying to free the resource, and then how you'd debug where that invalid pointer came from?)
At which point we arrive at the operator>>(), which can be overloaded for istreams and your class.
class Employee
{
public:
// ...
friend istream & operator>>( istream &, Employee & );
// ...
};
istream & operator>>( istream & in, Employee & e )
{
getline( e.name, in );
in >> e.salary;
return in;
}
int main()
{
Employee e; // For the sake of example, a default constructed employee
cout << "Enter new employee's name (on its own line), and salary:\n";
cin >> e;
}
And once there, you get an idea of what data encapsulation actually means: Employee is an object, not a data container. You should not poke around in it's innards, you should send messages.
Employee e( "Mark Smith", Role::INTERNEE, 10000 );
cout << e; // Output: "Mark Smith, Internee, with an income of 10.000 sicles."
e.promote( Role::TEAM_LEAD, 10000 ); // Exception: "Wrong pay scale for Team Leads."
e.promote( Role::TEAM_LEAD, 30000 );
cout << e; // Output: "Mark Smith, Team Lead, with an income of 30.000 sicles and a free car."
Note how the Employee class sanity-checked the new salary, and knew that a promotion to Team Lead automatically included a free car, and did that without you having to set that yourself. That is what this encapsulation is about...
As for your question "as asked", whether your implementation could do read a salary more elegantly, you could of course...
void setSalary(istream & in) {
in >> salary;
}
...and...
cout << "Enter salary:" << endl;
e.setSalary( cin );
And because that's properly encapsuled, you could add sanity checks, change the type of salary to BigNum, or start support reading in hieratic.
THIS QUESTION IS A GENERAL ANSWER WILL COVER YOU QUESTION
So in OOP, making and using only the copy or the OBJECT is a core concept, the way you are making a single copy OBJECT of a class is a standard one, but have you ever thought about MAKING AN ARRAY OF TYPE "YOUR CLASS's Objects"? because in your case the obj will only hold only one copy of Employee but if you use a data structure e.g "Array" for storing a number of objects then it will be a lot easier let me clarify for you
EXAMPLE
int a, b, c, d, e, f, g ... z;
assigning all of these alphabets a value will be a huge pain but wait you can have a data structure for it.
int arrayOfAlphabets[26];
now you want to assign values? (you can assign it dynamically but this is just an example to show you that you can just control the assigning of a variable with just a counter)
for(int x = 0 ; x < 26; x++){
arryOfAlphabets[x] = someNumber
}
Want to get Values? you can get values dynamically but this is just an example to show you that you can just control the setting & getting of a variable with just a counter)
for(int x = 0; x < 26; x++){
cout << "Value of Alphabet" + x + " is: " + arrayOfAlphabets[x] << endl;
}
now you have seen the example that making a data structure specific data type can remove a lot of pain of yours now keep in mind for the sake of understanding,
YOUR CLASS IS DATATYPE THAT YOU CREATED
class Employee {
private:
// Private attribute
int salary;
public:
// Setter
void setSalary(int s) {
salary = s;
}
// Getter
int getSalary() {
return salary;
}
};
and in main what you can do is
int main(){
Employee emp[5];
int tempSalery;
for( int i=0; i<5; i++ )
{
cin >> tempSalery;
emp[i].setSalery(tempSalery);
}
}
now to print out your result
for( int i=0; i<5; i++ ){
cout << "Employee " + x + "Salery is: " + emp[i].getSalery() << endl;
}
Can someone please help me with the below requirement
Class book that that contains attributes BookId, BookName, and Price. It also contain member function to input and show its attributes.
Write another class Writer that contains that contains the attributes of WriterName, Address and NumberofBooks written by him. It contains array of book objects as iys member. The length of array should be 5 to store the data of five books.
It should also contain a member function to input and display its attributes.
I found a solution on google with below code but it appears it is useful for my half requirement.
#include<iostream.h>
#include<stdio.h>
#include<conio.h>
class BOOK
{
int BOOKNO;
char BOOKTITLE[20];
float PRICE;
void TOTAL_COST(int N)
{
float tcost;
tcost=PRICE*N;
cout<<tcost;
}
public:
void INPUT()
{
cout<<"Enter Book Number ";
cin>>BOOKNO;
cout<<"Enter Book Title ";
gets(BOOKTITLE);
cout<<"Enter price per copy ";
cin>>PRICE;
}
void PURCHASE()
{
int n;
cout<<"Enter number of copies to purchase ";
cin>>n;
cout<<"Total cost is ";
TOTAL_COST(n);
}
};
void main()
{
BOOK obj;
obj.INPUT();
obj.PURCHASE();
getch();
}
To contain an instance of an object you declare a member of that type in your class:
class Writer
{
const size_t MAX_BOOKS_WRITTEN = 5U;
Book books_written[MAX_BOOKS_WRITTEN];
};
In the above class, "that contains that contains" an array of books written.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
For my assignment I have to make a program to read student data from standard input, sort it by last name / first name, and print out the result to standard output. Students consists of a last name, first name, and a grade point average. It says to there are no more than 50 students.
It also says you should not rely on an external library function to do the sort.
Here is the example:
Ware Henry 87.2
Dantes Edmond 91.4
Earhart Amelia 92.6
Here is how it should be sorted:
Dantes Edmond 91.4
Earhart Amelia 92.6
Ware Henry 87.2
Here is the code I have so far that is not working properly:
#include <iostream>
#include <string>
using namespace std;
class student
{
public:
student();
void input();
void output();
void sort();
private:
string Fname;
string Lname;
float gpa;
}
student::student()
{
Fname = "";
Lname = "";
gpa = 0;
}
void student::input()
{
cout << "Enter first name, last name, and gpa"<<endl;
cin >> Fname >> Lname >> gpa;
}
void student::sort()
{
char temp;
int count = 0;
int i = 1;
while (i < word.size()) {
if (word[i - 1] > word[i]) {
temp = word[i];
word[i] = word[i + 1];
word[i + 1] = temp;
i++;
if (i >= word.size())
{
alpha(word);
}
}
else
{
count++;
}
}
return word;
}
void main()
{
student.input();
}
Any advice on where I went wrong and any possible solutions?
your student class has member variables to hold only one student, you need 50 instances of class student.
then you should hold these instances in an array/vector (whatever for container you are allowed to use) I assume you need to use a raw array so something like this will do.
student* students = new student[50];
what you then need is a compare function in your class to be able to sort the array, the compare function knows the internals of your class and you can decide how you want to sort the list e.g. after surname.
the sort function could be inside your class declared as a static function or maybe more logically an external function since student is not a container of student instances.
don't forget to delete the array when done
delete [] students;
in real world problems you would use std containers and e.g. algorithm sort for this kind of work.
Your compiler must have told you that word and alpha used in sort method are undeclared identifiers and count is assigned but never used.
Solution: Never ever steal code from wild wild web without understanding what it does. You may take others' code as inspiration, but blind copy-paste is a big no-no.
To give you a next step: you haven't even stored your students, you just input one, how would you sort just one entry? Think (and code) what you should use to store them and start working from there on how to sort them.
Good luck.
There are several reasons why your code is not working, first reason is that your class definition needs semicolon ';' at the end to close it.
class student
{
public:
student();
void input();
void output();
void sort();
private:
string Fname;
string Lname;
float gpa;
}; //semicolon here
In your sort() method the variable word is used but never declared, coding is not magic, the variable must declared and initialized before it is used like in your code.
while (i < word.size()) { //word must be declared somewhere
Also, in the sort method, you call a function alpha(word), this function does not exist in your program.
The sort method is of type void, which means it will not return anything, yet you are trying to return a string.
Another problem with the sort method is that it is never called from anywhere and will never be run.
There is also a big problem here with the way you are trying to use your class. To use the methods of the class you first have to instantiate an object of that class, you can then access the methods of the class through the object like this:
int main()
{
student theStudent;
theStudent;
theStudent.input();
return 0;
}
Another fundamental problem is that you are only going to get data for one object like this, you need to store several objects in something like an array and then sort the objects in the array:
int main()
{
student students[5];
for (int i = 0; i < 5; i++)
{
student newStudent;
students[i] = newStudent;
newStudent.input();
}
sort(students);
delete[] students;
return 0;
}
Which brings us to the sort method. It would be wise to not do the sorting as a method of the student class, but as a function called from main instead (like in the example above), that way it is easy to sort the array of student objects from main.
These are a few advice to start with, i am not going to do your complete homework for you though, if you use google and some of your precious energy, i believe that you will succeed.
I'm learning C++ and having trouble with pointers.
This simple project consists in a invoice that has a pointer to a customer.
Classes:
class Customer {
string name;
public:
Customer(string name) { this->name = name; };
string getName() { return name; };
void changeName(string name) { this->name = name; };
};
class Invoice {
Customer * customer;
public:
Invoice(Customer *customer) { this->customer = customer; };
Customer getCustomer() { return *customer; };
};
Main:
Customer *customer1 = new Customer("Name 1");
Invoice invoice1(customer1);
cout << invoice1.getCustomer().getName() << endl; //Return:Name 1;
How can I use Customer::changeName(string name) in order to make this work:
(...) changeName("Name 2");
cout << invoice1.getCustomer().getName() << endl; //Return:Name 2;
I don't know what I should use to change the customer's name. Or maybe I'm doing something wrong in the class Invoice.
Why change the name through Invoice?
So I can learn how I can learn how to use the pointer before the project starts getting big.
Later I'm going to have a vector of Invoices and a vector of Customers. Getting the pointer to a Customer from a Invoice or from a vector of Customers should be the same.
Thank you,
Eduardo
Customer getCustomer() { return *customer; };
should be
Customer& getCustomer() { return *customer; };
because in the first case you copy the customer object and so your changes happen in a temporary object that gets thrown away...
in the second you will return a reference to the object you created.
to change name
string newName = "Edu";
invoice1.getCustomer().changeName( newName );
If you want this substantially hardened, i've taken such liberties here. Both Customer and Invoice declarations are significantly updated. Compare them to your existing code. Don't just copy this into your code, as it will definitely break a ton of things. Rather, look at it and see if it makes sense to you:
class Customer
{
string name;
public:
Customer(const string& name) : name(name) {};
const string& getName() const { return name; };
void changeName(const string& name) { this->name = name; };
};
class Invoice
{
const Customer& customer;
public:
Invoice(const Customer& customer) : customer(customer) {};
const Customer& getCustomer() const { return customer; };
};
In general (more often than not, anyway) the only times you should need to pass an object by pointer is if there is a chance the object pointer receiver should accept NULL as a valid value. Otherwise use references or smart pointers. Arrays of object pointers to support polymorphic access not withstanding (and even then, smart pointers ftw), this is generally a good rule to follow.
Significant changes made:
Uses const references unless there is specific need for non-const-access
Classes have initializer lists to ensure best-construction for member vars, and, in fact are required now for Invoice, since the Customer reference memeber must be initialized in an initializer list.
Main
Customer customer1("Name 1");
Invoice invoice1(customer1);
// note: invoice now only allows you to obtain a const-reference
// to the customer of the invoice. As such, you can only fire
// const-members on the returned customer reference.
cout << invoice1.getCustomer().getName() << endl; //Return:Name 1;
// without the const-ness of the getCustomer() member and the reference
// it returns, you would have been able to do this:
//
// invoice.getCustomer.changeName("newname");
//
// As it is written now, you can only change a customer name from
// a non-const customer reference (or pointer), and in doing so,
// *all* invoices for that customer will reflect this change.
customer1.changeName("Name 2");
// note: the invoice was not changed, but the customer it references
// was, and we should see that change now.
cout << invoice1.getCustomer().getName() << endl; //Return:Name 2;
I hope this gives you some ideas on how to restrict and harden your object access later on in your project.
In Invoice, return the pointer to the Customer itself, rather than a copy of its dereferenced value:
Customer* getCustomer() { return customer; };
Then you can change the name like so, and the change will affect the actual Customer object:
invoice1.getCustomer()->changeName("Name2")