Enumerations and User Input - c++

I was wondering if enumeration is commonly used with user input. I'm doing an exercise in which in my Book class I have to create an enum Genre with different genre enumerators such as fiction, non, fiction etc.
When the user uses the program, he/she is asked for certain information about the book being stored. For a genre, normally I would just do this with a string function and restrict it to certain names with if statements.
However, I'm not sure how to accomplish the same process with an enumerated type, nor do I know if it's even supposed to be used for that sort of thing. Here is the code if you're interested.
#include "std_lib_facilities.h"
//Classes-----------------------------------------------------------------------
class Book{
public:
Book(){}; // default constructor
//operators
friend ostream& operator<<(ostream& out, const Book& val);
bool Book::operator==(const Book& check)
//enumerators
enum Genre{
fiction, nonfiction, periodical, biography, children};
//member functions
string title();
string author();
int copyright();
void ISBN();
bool checkout();
private:
string title_;
string author_;
int copyright_;
int ISBN1;
int ISBN2;
int ISBN3;
char ISBN4;
bool checkout_;
};
// Error Function---------------------------------------------------------------
void _error(const string& s)
{
cout << endl;
cout << "Error: " << s << endl;
cout << endl;
}
// Member Functions-------------------------------------------------------------
string Book::title()
{
cout << "Title: ";
getline(cin,title_);
cout << endl;
return title_;
}
string Book::author()
{
cout << "Author: ";
getline(cin,author_);
cout << endl;
return author_;
}
int Book::copyright()
{
cout << "Copyright: ";
cin >> copyright_;
cout << endl;
return copyright_;
}
void Book::ISBN()
{
cout << "ISBN (Use spaces): ";
cin >> ISBN1 >> ISBN2 >> ISBN3 >> ISBN4;
if((ISBN1<0) || (ISBN2<0) || (ISBN3<0) || (ISBN1>9) || (ISBN2>9) || (ISBN3)>9)
_error("Must be single digit.");
else if(!isdigit(ISBN4) && !isalpha(ISBN4))
_error("Must be single digit or letter.");
else{ cout << endl;
return;}
}
bool Book::checkout()
{
char check;
cout << "Checked out?(Y or N): ";
cin >> check;
switch(check){
case 'Y':
cout << endl;
return true;
break;
case 'N':
cout << endl;
return false;
break;
default:
_error("Must be Y or N.");}
}
// Operator Overloads-----------------------------------------------------------
ostream& operator<<(ostream& out, const Book& val){
out << "Title: " << val.title_ << endl;
out << "Author: " << val.author_ << endl;
out << "ISBN: " << val.ISBN1 << "-" << val.ISBN2 << "-" << val.ISBN3 << "-" << val.ISBN4 << endl;
out << endl;
return out;}
bool Book::operator==(const Book& check){
return((ISBN1 == check.ISBN1) && (ISBN2 == check.ISBN2) && (ISBN3 == check.ISBN3)
&& (ISBN4 == check.ISBN4));}
// Main-------------------------------------------------------------------------
int main()
{
bool finished = false;
char notfinished;
while(!finished)
{
Book book;
book.title();
book.author();
book.copyright();
book.ISBN();
book.checkout();
cout << "Do you wish to store another book?(Y or N): ";
cin >> notfinished;
if(notfinished == 'Y'){
cin.ignore();
cout << endl;}
else if(notfinished == 'N') finished = true;
else _error("Must be Y or N");
}
keep_window_open();
}
Note that some things aren't being used at the moment because the feature they are a part of hasn't been fully implemented yet (storing in a library, outputting books, etc.)
So what would it take to accept user input for the enumerators listed, if even possible? I was thinking something along the lines of making a Genre variable. Then having a function where the user inputs for cin>>variable. However, I'm guessing that the function wouldn't understand an input like 'fiction' and would only accept the enumerator values and input.

Make Genre a class that wraps the enum type (GenreTypeEnum). Add the necessary operators, e.g. istream, ostream, equal operator, etc.
Inside the istream operator, you can read a std::string from the stream and then parse and convert the value to the associated GenreTypeEnum.
Something like this perhaps:
namespace GenreType { enum GenreTypeEnum { miscellaneous, fiction, non_fiction, children }; }
class Genre
{
public:
Genre() : genreType( GenreType::miscellaneous) {}
~Genre() {}
void setType( std::string genreTypeString ){ // implement string-> enum }
std::string toString( void ) const { // convert genre back to string }
private:
GenreType::GenreTypeEnum genreType;
};
std::ostream& operator<<( std::ostream& os, const Genre& genre )
{
os << genre.toString();
return os;
}
std::istream& operator>>( std::istream& is, Genre& genre )
{
std::string input;
is >> input;
genre.setType( input );
return is;
}

C-style enums are not terribly useful for this purpose, since there's no way to recover the original string name. You could make some switch-based mechanism, but at that point you may as well just set up your own way of doing it all that works with your user I/O requirements without shoehorning.

One of the ways to handle this is to set up a map of strings to enum values. Other possibilities include a dedicated function.
See this question for some ideas.
This question has some ideas of how to generate code to convert enums to strings, but most of the examples will work in reverse as well.

Related

Unable to populate array of structures

my program is supposed to parse through strings from a file and store in the array of structs.
Example: Skyfall, 1.109, Sam Mendes, 11/9/12, 143. Program will parse the string and store the title, gross, director name etc.
Whenever i run the code it doesn't seem to store it properly.
Also, I'm getting this error.
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Here's my struct:
struct Movie
{
string Title; // Movie title
string Gross; // Gross total in billion dollars
string Director; // Director name
string Date; // Release date
string Runtime; // Runtime in minutes
};
This function is to create the array of objects and open the file
Movie* createDatabase(int& number_Of_Lines)
{
// input file
ifstream movie_file;
string filename;
do
{
cout << "Please enter filename: " ;
getline (cin , filename);
movie_file.open(filename.c_str());
if(movie_file.fail())
cout << "Invalid file" << endl ;
}while(movie_file.fail());
// array of objects
number_Of_Lines = numberOfLines(movie_file);
Movie* ptr = new Movie [number_Of_Lines];
//Looping through array of objects
for(int i = 0 ; i < number_Of_Lines ; i++)
populateMovieFromFile(movie_file, ptr[i]);
return ptr;
}
This function populates the objects
void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
getline(movie_file, movies.Title, ',');
movie_file.ignore();
getline(movie_file, movies.Gross, ',');
movie_file.ignore();
getline(movie_file, movies.Director, ',');
movie_file.ignore();
getline(movie_file, movies.Date, ',');
movie_file.ignore();
getline(movie_file, movies.Runtime);
}
Full program:
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <cctype>
using namespace std;
struct Movie
{
string Title; // Movie title
string Gross; // Gross total in billion dollars
string Director; // Director name
string Date; // Release date
string Runtime; // Runtime in minutes
};
int numberOfLines(ifstream&);
void populateMovieFromFile(ifstream&, Movie&);
void displayMovie(const Movie&);
Movie* createDatabase(int&);
bool caseInsensitiveCmp(string, string);
void findMovie(Movie*, int);
void saveToFile(const Movie&);
bool promptToContinue();
//void displayFavorites();
int main ()
{
int number_Of_Lines = 0;
Movie* ptr_movies = createDatabase(number_Of_Lines);
do
{
findMovie(ptr_movies , number_Of_Lines);
}while (!promptToContinue());
//displayFavorites();
return 0;
}
int numberOfLines(ifstream& movie_file)
{
int number_Of_Lines = 0;
string lines;
if(movie_file)
{
while(getline(movie_file, lines))
number_Of_Lines++ ;
}
movie_file.seekg (0, ios::beg);
return number_Of_Lines;
}
void populateMovieFromFile(ifstream& movie_file, Movie& movies)
{
getline(movie_file, movies.Title, ',');
movie_file.ignore();
getline(movie_file, movies.Gross, ',');
movie_file.ignore();
getline(movie_file, movies.Director, ',');
movie_file.ignore();
getline(movie_file, movies.Date, ',');
movie_file.ignore();
getline(movie_file, movies.Runtime);
}
void displayMovie(const Movie& movie)
{
cout << right << setw(13) << "Title: " << left << movie.Title << endl;
cout << right << setw(13) << "Gross Total: " << left << movie.Gross << " billion dollars" << endl;
cout << right << setw(13) << "Director: " << left << movie.Director << endl;
cout << right << setw(13) << "Release date: " << left << movie.Date << endl;
cout << right << setw(13) << "Runtime: " << left << movie.Runtime << " minutes" << endl;
}
Movie* createDatabase(int& number_Of_Lines)
{
// input file
ifstream movie_file;
string filename;
do
{
cout << "Please enter filename: " ;
getline (cin , filename);
movie_file.open(filename.c_str());
if(movie_file.fail())
cout << "Invalid file" << endl ;
}while(movie_file.fail());
// array of objects
number_Of_Lines = numberOfLines(movie_file);
Movie* ptr = new Movie [number_Of_Lines];
//Looping through array of objects
for(int i = 0 ; i < number_Of_Lines ; i++)
populateMovieFromFile(movie_file, ptr[i]);
return ptr;
}
bool caseInsensitiveCmp(string input, string list) //list will be from the object of array
{
int i = 0 , j = 0;
while (input[i])
{
char c = input[i];
input[i] = tolower(c);
i++;
}
while (list[j])
{
char c = list[j];
list[j] = tolower(c);
j++;
}
if (input == list)
return true;
else
return false;
}
void findMovie(Movie* ptr_movie, int number_Of_Lines)
{
cout << endl;
int i = 0;
char save;
string input_title;
bool found = false;
bool No_Match = false;
cout << "Enter a movie title to search for: ";
getline(cin , input_title);
do
{
found = caseInsensitiveCmp(ptr_movie[i].Title , input_title); //loop it
if (found == false)
i++;
if (i>=number_Of_Lines)
No_Match = true;
} while (found == false || No_Match == false);
if(found == true)
{
displayMovie(ptr_movie[i]);
cout << endl ;
cout << "Would you like to save the above movie? (Y or N)" << endl;
cin >> save;
if (save == 'y' || save == 'Y')
saveToFile(ptr_movie[i]);
}
else
cout << input_title << " not found in database. Please try again." << endl;
}
void saveToFile(const Movie& movie)
{
ofstream outfile;
outfile.open("favourites.txt", ios::app);
cout << movie.Title << "," << movie.Gross << ","
<< movie.Director << "," << movie.Date << ","
<< movie.Runtime << endl;
}
bool promptToContinue()
{
char Quit;
bool exit = false;
cout << "Would you like to exit? (Y or N): ";
cin >> Quit;
switch (Quit)
{
case 'Y':
case 'y':
exit = true;
case 'N':
case 'n':
exit = false;
}
return exit;
}
Any help is appreciated
The problem is:
In your "saveToFile" function, you open a ofstream, but do not use it. Instead you write to std::cout. So, you do not store the data.
Additionally, you are calling new but never delete. With that you are creating a memory leak.
Then, you still thinking to much in C. You should not use new or plain C-Style arrays. Never.
You should instead use STL containers and a more object oriented approach. At the moment you are using a lot of global functions, working with data.
In C++ you should use objects and associated data and methods. For example, a Movie knows how to read and store its data. Therefore implement that as a method.
And a Movie database is an additional object that contains a vector of movies.
To give you an idea of a more object oriented approach, I create a small example for you.
Please see and try to understand.
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
#include <string>
class Movie
{
public:
// Overload Extractor Operator to read data from somewhere
friend std::istream& operator >> (std::istream& is, Movie& m);
// Overload Inserter operator. Insert data into output stream
friend std::ostream& operator << (std::ostream& os, const Movie& m);
// Show movie data on std::out
void display() const;
// Check if a movie has a certain title
bool hasTitle(const std::string& t) const { return t == title; }
private:
// Data
std::string title{}; // Movie title
std::string gross{}; // Gross total in billion dollars
std::string director{}; // Director name
std::string date{}; // Release date
std::string runtime{}; // Runtime in minutes
};
// Overload Extractor Operator to read data from somewhere
std::istream& operator >> (std::istream& is, Movie& m) {
std::vector<std::string> dataInOneLine{}; // Here we will store all data that we read in one line;
std::string wholeLine; // Temporary storage for the complete line that we will get by getline
std::regex separator(","); ; // Separator for a CSV file
std::getline(is, wholeLine); // Read one complete line
// Parse the line and split it into parts
std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1),
std::sregex_token_iterator(),
std::back_inserter(dataInOneLine));
// If we have read all expted strings, then store them in our struct
if (dataInOneLine.size() == 5) {
m.title = dataInOneLine[0];
m.gross = dataInOneLine[1];
m.director = dataInOneLine[2];
m.date = dataInOneLine[3];
m.runtime = dataInOneLine[4];
}
return is;
}
std::ostream& operator << (std::ostream& os, const Movie& m) {
// Copy csv data to ostream
return os << m.title << "," << m.gross << "," << m.director << "," << m.date << "," << m.runtime << "\n";
}
void Movie::display() const {
std::cout << " Title: " << title << "\n Gross Total: " << gross << " billion dollars\n Director: " << director
<< "\nRelease date: " << date << "\n Runtime: " << runtime << " minutes\n";
}
// Database for Movies
class MovieDatabase {
public:
// Constructor. Open and read the database
explicit MovieDatabase(const std::string pafn) : pathAndFileName(pafn) { open(); }
// Destructor automatically saves and closes the database
~MovieDatabase() { close(); };
// Open/close the database
bool open();
void close();
// Add a new movie
void addMovie(const Movie& m) { data.push_back(m); }
// Find and display a movie
bool findAndDisplay (const std::string& title);
private:
const std::string pathAndFileName{};
std::vector<Movie> data{};
};
// Destructor
void MovieDatabase::close() {
// Save data
std::ofstream outFileStream{ pathAndFileName, std::ios::trunc };
if (outFileStream) {
// then save all data in csv format
std::copy(data.begin(), data.end(), std::ostream_iterator<Movie>(outFileStream));
}
}
// Open database and read the data from disk
bool MovieDatabase::open() {
bool success{ false };
// Open the file
std::ifstream inFileStream{ pathAndFileName };
// If the file could be opened
if (inFileStream) {
success = true;
// Then copy all data from disk, parse the csv and store it in our data vector
std::copy(std::istream_iterator<Movie>(inFileStream), std::istream_iterator<Movie>(), std::back_inserter(data));
}
return success;
}
// Find and display a value
bool MovieDatabase::findAndDisplay (const std::string& title) {
bool found { false };
// Search for a given title
std::vector<Movie>::iterator md = std::find_if(data.begin(), data.end(), [&title](const Movie &m) { return m.hasTitle(title); });
if (data.end() != md) {
// If found, then display it
md->display();
found = true;
}
else {
std::cerr << "\n\nTitle '" << title << "' not found in database\n\n";
}
return found;
}
int main() {
// Get the name of the database
std::string pathNameDatabase{};
std::cout << "Enter the path/filename of the database:\n";
std::cin >> pathNameDatabase;
// Define database and open it
MovieDatabase md{ pathNameDatabase };
// Do some stuff
std::cout << "\n\nSearch for title. Please enter title:\n";
std::string title{};
std::cin >> title; std::cin.ignore();
// Search and display data
md.findAndDisplay(title);
// Add a new record
std::cout << "\n\nAdd new movie data\nPlease enter title, gross, director, date, runtime (in one line, seperated by comma):\n";
Movie m{};
std::cin >> m;
m.display();
md.addMovie(m);
return 0;
}

Adding Setters to a function template

I am trying to add setter to my project as a requirement but am stuck on making it work. I commented out the areas I have to add it but everything I have looked up has not worked. I know I am close but everything seems to not work.
#include "pch.h"
#include <string>
#include <conio.h>
#include <iostream>
using namespace std;
template <class T>
class Holder
{
private:
T thing;
int number; //add an integer data member that stores the number of data members of whatever class is stored in "thing"
public:
void standardInput();
void standardOutput();
void setNumber(int); // declare a setter function for the new data member
};
template <class T>
void Holder<T>::standardInput()
{
cout << endl;
cout << "You will be asked to enter " << n.setNumber << " items" << endl; // a line of output that use the data member with the number
cin >> Holder<T>::thing;
}
template <class T>
void Holder<T>::standardOutput()
{
cout << endl;
cout << "Here's the data you requested: " << endl;
cout << Holder<T>::thing << endl;
}
template<class T>
void Holder<T>::setNumber(int n) //implement the setter function for the new data member
{
setNumber = n;
}
// This is the first of two custom classes
class Student
{
friend ostream& operator<<(ostream&, Student&);
friend istream& operator>>(istream&, Student&);
private:
string name;
double tuiton;
};
ostream& operator<<(ostream& out, Student& a)
{
out << "The Student " << a.name << " Tuiton is: " << a.tuiton << endl;
return out;
}
istream& operator>>(istream& in, Student& a)
{
cout << "Enter the name of student: ";
in >> a.name;
cout << "What is the Price of tuiton? ";
in >> a.tuiton;
return in;
}
// This is the second of two custom classes
class FastFood
{
friend ostream& operator<<(ostream&, FastFood&);
friend istream& operator>>(istream&, FastFood&);
private:
int valueNumber;
double cost;
};
ostream& operator<<(ostream& out, FastFood& a)
{
out << " The Combo Number is " << a.valueNumber << " .It costs " << a.cost << endl;
return out;
}
istream& operator>>(istream& in, FastFood& a)
{
cout << "What is the Combo number? ";
in >> a.valueNumber;
cout << "Enter cost: ";
in >> a.cost;
return in;
}
int main()
{
cout << "For an integer: " << endl;
Holder<int> val;
// use the setter to store 1.
val.standardInput();
val.standardOutput();
cout << "For a Student:" << endl;
Holder<Student> aCollegeStudent;
// use the setter to store 2.
aCollegeStudent.standardInput();
aCollegeStudent.standardOutput();
Holder<FastFood> vMeal;
// use the setter to store 3.
vMeal.standardInput();
vMeal.standardOutput();
cout << endl;
system("pause");
return 0;
}
This is supposed to ask the integer along with the student and fastfood information. I required to add a setter in the template but wont need to use a getter due to standard input and output just cant figure out the right way to do it.

How do I go about printing a vector of objects?

I'm guessing I might have to use pointers, but haven't gone in depth too much on them in class yet to try and implement them in my program. I have this so far, the printing function is towards the middle of the program. I'm not quite sure on how to print out the elements from the vector as my approach didn't work.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class rolodex
{
string name;
string street, town, state;
string zip;
string phone;
vector <rolodex> entries;
public:
rolodex();
void getmenu();
void add_entry();
void set_name();
void set_address();
void set_phone();
void printinfo();
};
rolodex :: rolodex() : name(""), street(""), town(""), state(""), zip(""),
phone(""), entries()
{
}
void rolodex :: getmenu()
{
cout << "\n\n1)Add Entry";
cout << "\n5)Print All Entries";
cout << "\n6)Exit" << endl;
}
void rolodex :: add_entry()
{
rolodex temp;
cout << "\n\nEnter Name: ";
temp.set_name();
temp.set_address();
cout << "\n\nEnter Your Phone Number: ";
temp.set_phone();
entries.push_back(temp);
}
void rolodex :: set_name()
{
cin.ignore();
getline(cin, name);
}
void rolodex :: set_address()
{
cout << "\n\nNow we'll enter address information.";
cout << "\n\nStreet: ";
getline(cin, street);
cout << "\n\nTown: ";
getline(cin, town);
cout << "\n\nState: ";
getline(cin, state);
cout << "\n\nZip: ";
getline(cin, zip);
}
void rolodex :: set_phone()
{
getline(cin, phone);
}
void rolodex :: printinfo()
{
for(unsigned int i = 0; i < entries.size(); i++)
{
cout << entries[i] << endl; //This is where I'm stuck since I've only
//worked with vectors of non-object data
//type
}
}
int main()
{
rolodex person, menu;
short choice;
bool done = false;
do
{
menu.getmenu();
cout << "\n\nEnter a choice: ";
cin >> choice;
switch(choice)
{
case 1:
person.add_entry();
break;
case 5:
person.printinfo();
break;
case 6:
done = true;
break;
default:
cout << "\n\nInvalid Entry." << endl << endl;
}
} while(!done && isdigit(choice));
return 0;
}
πάντα ῥεῖ is right, but to add a little more detail...
You need to specify how you want the stream to handle your object. This is done by by adding a << operator. For example:
std::ostream& operator<<(std::ostream& s, const rolodex& r){
// Or however you want to format it.
s << "Name: " << r.name << " : ";
s << "Street: " << r.street << " : ";
s << "Town: " << r.town << " : ";
s << "State: " << r.state << " : ";
s << "Zip: " << r.zip << "\n";
}
Unfortunately, the function above tries to access the private fields of your class, which it can't because it is not part of the class definition.
An easy way to address that is to declare this function a "friend" inside of the class definition, like such:
friend std::ostream& operator<<(std::ostream&, const rolodex&);
...And since you might appreciate it, one big copy-pasteable chunk that you can use directly that should make your function work:
class rolodex
{
string name;
string street, town, state;
string zip;
string phone;
vector <rolodex> entries;
public:
rolodex();
void getmenu();
void add_entry();
void set_name();
void set_address();
void set_phone();
void printinfo();
friend std::ostream& operator<<(std::ostream&, const rolodex&);
};
std::ostream& operator<<(std::ostream& s, const rolodex& r){
// Or however you want to format it.
s << "Name: " << r.name << " : ";
s << "Street: " << r.street << " : ";
s << "Town: " << r.town << " : ";
s << "State: " << r.state << " : ";
s << "Zip: " << r.zip << "\n";
}
Following up on πάντα ῥεῖ's suggestion, here's one way of doing that, changing your design as little as possible:
1) Create a non-member overloaded operator<< for your rolodex class:
std::ostream& operator<< (std::ostream& os, const rolodex& rol)
{
os << rol.name << ":" << std::endl
<< "\t" << rol.street << std::endl
<< "\t" << rol.town << std::endl
<< "\t" << rol.state << std::endl
<< "\t" << rol.zip << std::endl
<< "\t" << rol.phone << std::endl;
return os;
}
.. but the compiler will chide you for attempting to access private members (by default, members are private) from outside the class, so you would have to relax the rules a bit:
class rolodex
{
...
public:
...
friend std::ostream& operator<< (std::ostream& os, const rolodex& rol);
};
You can't have the operator<< inside the class itself, see does-overloading-operator-works-inside-the-class.
However, it is almost always better design to add getter functions to your public interface anyway. You would have get_name() etc in the public: section of your class def, those functions would initially just return the values of the private member variables, and then your operator<< can use them instead of trying to access the private members. You then no longer require the friend declaration.
I upvoted Some programmer dude's remark about your design
The code for letting the use input the data really shouldn't be inside the rolodex class, because it makes the class hard to reuse. Image wanting to re-use the rolodex from a graphical interface, for example, and it's not such a good idea to have the rolodex contain instances of itself inside the vector.
I would suggest a
1) Person class containing all the person's attributes, with public getters get_name() and setters set_name() that don't use a specific entry method, just take the data as arguments e.g. set_name(std::string& name).
2) an non-member operator<< to output a person to an output stream
3) a Rolodex class with a private std::vector<Person> and methods to add a person, write all the persons to an output stream, etc..
Good luck & enjoy :-)
Edit: the menu structure on the terminal should IMHO be left inside the main() function or encapsulated into another class. But certainly don't leave it in Rolodex or worse, Person.

Error when overloading opertor>> for string memebrs in the class

When I input the value for the string types, for examples:
_ho I typed Peter
_hoten I typed Peter Parker
_ten I typed Marry
My output on the screen was:
Peter Peter Marry
Here is my code:
class SinhVien
{
private:
string _ho;
string _tenlot;
string _ten;
public:
static int InstanceCount;
SinhVien();
string ToString() const;
friend istream& operator>>(istream& in, SinhVien* p);
friend ostream& operator<<(ostream& out, const SinhVien* p);
~SinhVien();
};
istream& operator>>(istream& in, SinhVien *p)
{
cout << "Nhap ho: \n";
in >> p->_ho;
rewind(stdin);
cout << "Nhap ten lot: \n";
in >> p->_tenlot;
rewind(stdin);
cout << "Nhap ten: \n";
in >> p->_ten;
return in;
}
string SinhVien::ToString() const
{
stringstream writer;
writer << _ho << " " << _tenlot << " " << _ten << "\n";
return writer.str();
}
ostream& operator<<(ostream &out, const SinhVien* p)
{
out << p->ToString();
return out;
}
void main()
{
SinhVien *a;
a = new SinhVien();
cin >> a;
cout << a;
cout << "\nTo string:\n";
cout << a->ToString();
delete a;
system("pause");
}
In your std::basic_istream::operator>> overload, you need to use std::geline() instead of std::cin >>, so that you can get complete input name with spaces.
Secondly, you should pass the reference of the object pointer, so that the change will be applied to the passed object, not to its copy.
Fix:
std::istream& operator>>(std::istream& in, SinhVien* &p)
{ // ^^ take ref of object you pass, so that the change will be applied to the object, not to the copy of it.
std::cout << "Nhap ho: \n";
std::getline(in, p->_ho); // change
std::rewind(stdin);
std::cout << "Nhap ten lot: \n";
std::getline(in, p->_tenlot); // change
std::rewind(stdin);
std::cout << "Nhap ten: \n";
std::getline(in, p->_ten); // change
return in;
}
Above will work for one single input. However, the case of multiple inputs and use of std::cin >> in the main() can cause again some input skipping problems. Thanks to #Yksisarvinen pointing this out.
You can read more in this SO post: Why does std::getline() skip input after a formatted extraction?
Side note: In modern C++, you do not need to manage the raw pointers, anymore. Because you have smart pointers which will manage your object's lifetime as it goes out of scope. So use it whenever its possible.
That means you can do something like this: SEE LIVE
#include <memory>
class SinhVien
{
private: // memebrs
public:
// other member functions
friend istream& operator>>(istream& in, const std::unique_ptr<SinhVien> &p);
friend ostream& operator<<(ostream& out, const std::unique_ptr<SinhVien> &p);
};
std::istream& operator>>(std::istream& in, const std::unique_ptr<SinhVien> &p)
{
std::cout << "Nhap ho: \n";
std::getline(in, p->_ho); // change
std::cout << "Nhap ten lot: \n";
std::getline(in, p->_tenlot); // change
std::rewind(stdin);
std::cout << "Nhap ten: \n";
std::getline(in, p->_ten); // change
return in;
}
std::ostream& operator<<(std::ostream &out, const std::unique_ptr<SinhVien> &p)
{
return out << p->ToString();
}
int main()
{
auto a = std::make_unique<SinhVien>();
std::cin >> a;
std::cout << a;
std::cout << "\nTo string:\n";
std::cout << a->ToString();
return 0;
}

how to create a proper bool Employee::operator==(const Employee & rhs) const

I'm fairly new to C++ programming. I currently have an
error C2678: binary '==': no operator found which takes a left-hand operand of type 'const Employee'
This error is point to line
if (*iLoc == aID)
I believe I am suppose to create a
bool Employee::operator==(const Employee & rhs) const
{
}
that will override the error. I think I need to make a overloaded == operator that is able to compare and unsigned int to an Employee object. But I am stuck. I was wondering if anyone is able to help. Part of my main.cpp is included.
bool Employee::operator==(const Employee & rhs) const
{
}
void searchItem(const List<Employee>& l1)
{
if (l1.size() == 0)
cout << "List empty!\n";
else
{
unsigned int ID;
cout << "Enter ID#: ";
cin >> ID;
size_t index = 0;
List<Employee>::const_iterator iLoc = l1.begin();
while (iLoc != l1.end())
{
if (*iLoc == aID)
{
cout << "Employee's Name: " << aID << " found at node # " << index << endl;
break;
}
index++;
iLoc++;
}
if (iLoc == l1.end())
cout << "ID# " << aID << " not found!\n";
}
}
void removeItem(List<Employee>& l1)
{
if (l1.size() == 0)
cout << "List empty!\n";
else
{
unsigned int aID;
cout << "Enter ID#: ";
cin >> aID;
size_t index = 0;
List<Employee>::iterator iLoc = l1.begin();
while (iLoc != l1.end())
{
if (*iLoc == aID)
{
cout << "Value " << aID << " removed from node # " << index << endl;
l1.erase(iLoc);
break;
}
index++;
iLoc++;
}
if (iLoc == l1.end())
cout << "Value not found!\n";
}
}
In the function void removeItem(List<Employee>&), in the line
if (*iLoc == aID)
you are comparing a dereferenced iterator, i.e. an Employee, to an unsigned int (aID). The right hand side must be an Employee also, or Employee must have an implicit constructor that takes an unsigned int as a parameter, so the compiler may be able to convert aID to Employee(aID) when trying to resolve the call to (*iLoc).operator==(aID).
Solution: define either
bool Employee::operator==(const Employee & rhs) const
{
return comparison_here;
}
and make sure Employee has a constructor that takes an unsigned int, or create an overloaded operator== that takes an unsigned int,
bool Employee::operator==(unsigned int rhs) const
{
return comparison_here;
}
You may also consider defining operator== as a non-member function, so it can also take unsigned int as a left-hand-side parameter. More details: Operator overloading