How to improve operator overloading for my class? - c++

I have started learning C++. My teacher gave an assignment. I completed it (some polishing work is left) and everything seems to work but there is redundancy. My major issue is overloading.
How to improve overloading in my class. Rather than writing all four functions (two for fstream and two for iostream), is it possible to write only two ? Any other suggestions to improve the code further ?
#include <iostream>
#include <fstream>
#include <string>
#include "../../my_func.hpp"
#define usi unsigned short int
using namespace std;
class book
{
public:
usi book_id = 0, price = 0, no_of_pages = 0, year_of_publishing = 0;
string author_name = "NONE", publisher = "NONE";
book(usi b_id = 0, usi b_price = 0, usi b_no_of_pages = 0, usi b_year_of_publishing = 0,
const string& b_author_name = "NONE", const string& b_publisher = "NONE")
{
book_id = b_id;
price = b_price;
no_of_pages = b_no_of_pages;
year_of_publishing = b_year_of_publishing;
author_name = b_author_name;
publisher = b_publisher;
}
friend fstream& operator >> (fstream& is, book& obj);
friend fstream& operator << (fstream& os, const book& obj);
friend istream& operator >> (istream &is, book& obj);
friend ostream& operator << (ostream &os, const book& obj);
};
fstream& operator >> (fstream &is, book& obj)
{
char ch;
is >> obj.book_id >> obj.price
>> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); //To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
fstream& operator << (fstream &os, const book& obj)
{
os.operator<<(obj.book_id) << '\n' //calling operator function cuz it works
<< obj.price << '\n'
<< obj.no_of_pages << '\n'
<< obj.year_of_publishing << '\n';
os << obj.author_name << '\n'
<< obj.publisher << '\n';
return os;
}
istream& operator >> (istream &is, book& obj)
{
is >> obj.book_id >> obj.price
>> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); //To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
ostream& operator << (ostream &os, const book& obj)
{
os << obj.book_id << '\n'
<< obj.price << '\n'
<< obj.no_of_pages << '\n'
<< obj.year_of_publishing << '\n'
<< obj.author_name << '\n'
<< obj.publisher << '\n';
return os;
}
int main()
{
string path = ".\\C++_Experiment\\Exp-7\\Files\\Source.txt";
book b1(12, 3000, 100, 2003, "Lol", "Pew"), b2, b3;
fstream fio;
fio.open(path, ios::out | ios::app | ios::in);
if(fio) fio << b1;
else cout << "error";
fio.seekg(0, ios::beg);
if(fio) fio >> b2 >> b3;
cout << b2 << b3;
fio.close();
cout << "DONE";
return 0;
}

You only need two overloads here. ifstream and ofstream inherit from istream and ostream respectively so if you have
friend istream& operator >> (istream &is, book& obj);
friend ostream& operator << (ostream &os, const book& obj);
then those will work with cout and cin, and any fstream or stringstream objects as they also inherit from istream and ostream.

Some other suggestions apart from overloading only for std::istream and std::ostream:
Don't using namespace std;. Prefer to prefix your STL types and algorithms with std::.
Prefer using to typedef, and, in this case, to #define. You could limit the usi alias to the book scope.
Turn book into a struct: if everything within the class book is public. This way, you don't need the operator overloads to be friends of book, since they can directly access the members within a book instance.
Prefer to use member initialization lists, i.e. initialize your member variables right after the list of parameters. You can also provide a default member initialization value at the declaration point; but, in this case, a single {} will zero initialize the usi types. I wouldn't use default parameter values for the constructor in this example.
Consider that, if you provide a custom constructor, the default constructor, copy constructor, move constructor, copy assignment operator, and move assignment operator will be deleted, and thus not available.
[Demo]
#include <iostream>
#include <string>
struct book {
using usi = unsigned short int;
usi book_id{};
usi price{};
usi no_of_pages{};
usi year_of_publishing{};
std::string author_name{"NONE"};
std::string publisher{"NONE"};
book() = default;
book(const book& other) = default;
book& operator=(const book& other) = default;
book(book&& other) noexcept = default;
book& operator=(book& other) noexcept = default;
~book() = default;
book(usi b_id,
usi b_price,
usi b_no_of_pages,
usi b_year_of_publishing,
const std::string& b_author_name,
const std::string& b_publisher)
: book_id{b_id}
, price{b_price}
, no_of_pages{b_no_of_pages}
, year_of_publishing{b_year_of_publishing}
, author_name{b_author_name}
, publisher{b_publisher}
{}
};
std::istream& operator>>(std::istream& is, book& obj) {
is >> obj.book_id >> obj.price >> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); // To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
std::ostream& operator<<(std::ostream& os, const book& obj) {
return os
<< "["
<< obj.book_id << ", "
<< obj.price << ", "
<< obj.no_of_pages << ", "
<< obj.year_of_publishing << ", "
<< obj.author_name << ", "
<< obj.publisher << "]";
}
int main() {
book b1{ 12, 3000, 100, 2003, "Lol", "Pew" };
std::cout << "b1: " << b1 << "\n";
book b2{};
std::cin >> b2;
std::cout << "b2: " << b2;
}
Make book an aggregate. You can get rid of a lot of code if you forget about initializing the std::string members to "NONE" (you can always check for empty string at operator<< and output "NONE if needed). You get rid of the custom constructor and, with it, of all the other default constructors and assignment operators.
[Demo]
#include <iostream>
#include <string>
struct book {
using usi = unsigned short int;
usi book_id{};
usi price{};
usi no_of_pages{};
usi year_of_publishing{};
std::string author_name{};
std::string publisher{};
};
std::istream& operator>>(std::istream& is, book& obj) {
is >> obj.book_id >> obj.price >> obj.no_of_pages >> obj.year_of_publishing;
is.ignore(1, '\n'); // To take care of new line character
getline(is, obj.author_name);
getline(is, obj.publisher);
return is;
}
std::ostream& operator<<(std::ostream& os, const book& obj) {
return os
<< "["
<< obj.book_id << ", "
<< obj.price << ", "
<< obj.no_of_pages << ", "
<< obj.year_of_publishing << ", "
<< (obj.author_name.empty() ? "NONE" : obj.author_name) << ", "
<< (obj.publisher.empty() ? "NONE" : obj.publisher) << "]";
}
int main() {
book b1{ 12, 3000, 100, 2003, "Lol", "" };
std::cout << "b1: " << b1 << "\n";
book b2{};
std::cin >> b2;
std::cout << "b2: " << b2;
}

Related

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;
}

C++, How to read data from file and insert in container: Map

I have a problem with fill container map with information . I read the information with operator>> but it gives me these errors in my second class CDealer. no operator ">>" matches these operands and binary">>": no operator found which takes a right-hand operand of type 'const CStock'(or these is no acceptable conversion)
I have three classes: CStock, CDealer who have map<CStock, pair<unsigned, double>> Stock; and CShop who have vector<CDealer*> Dealers;
I'll be very thankful if someone can help me.
This is my operators << and >> in CStock
ostream &operator << (ostream &toStream, const CStock &S){
toStream << "Name Stock: " << S.m_strName_stock << " Producer: " << S.m_strProducer <<
"Date: " << S.Date;
return toStream;
}
istream &operator >> (istream &fromStream, CStock &S)
{return fromStream >> S.m_strName_stock >> S.m_strProducer >> S.Date;}
This is my operators << and >> in CDealer
`
ostream &operator << (ostream &toStream, CDealer &D)
{
toStream << "Name Dealer: " << D.m_strName_Dealer << " Agent: " << D.m_strAgent <<
"Address: " << D.m_strAddress;
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first <<it->second.first << it->second.second;
}
return toStream;
}
istream &operator >> (istream &fromStream, CDealer &D)
{
map<CStock, pair<unsigned, double>>::iterator it = D.Stock.begin();
fromStream >> D.m_strName_Dealer >> D.m_strAgent >> D.m_strAddress;
while (it!= D.Stock.end())
{fromStream >> it->first >> it->second.first >> it->second.second;}
return fromStream
}
And this is the constructor with parameter: file name and these tho operators <<, >>
CShop::CShop(const string &fname)
{
CDealer c;
fstream File(fname, ios::in);
if (File.is_open())
{
File >> m_strNameShop;
File >> m_strCity;
while (File>>c)
{
Dealers.push_back(new CDealer(c));
}
File.close();
}
else
throw "ERROR! ";
}
ostream &operator << (ostream &toStream, const CShop &S)
{
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer* >::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
CDealer* dealerPtr = *it++;
toStream << *dealerPtr<< endl;
}
return toStream;
}
istream &operator >> (istream &fromStream, CShop &D)
{
return fromStream >> D.m_strNameShop >> D.m_strCity;
}
And in the end main()
#include"CDealer.h"
#include"CShop.h"
#include<iostream>
#include<string>
#include <stdlib.h>
#include<vector>
using namespace std;
int main()
{
CShop SS1("data.txt");
cout << SS1;
system("pause");
return 0;
}
The implmentation of istream &operator >> (istream &fromStream, CDealer &D)
is poorly thought through.
The objective of the function is to read data from an input stream and flesh out the contents of the CDealer object. Normally the operator<< and operator>> functions work in tandem so that what you write using operator<< can be read back using operator>>.
From that point of view, even the operator<< function needs to revamped.
Here's one implementation that should work as long as there are no spaces in the string objects. If you have space in your string objects, the code needs be changed.
std::ostream& operator<<(std::ostream &toStream, CDealer cosnt& D)
{
// Write the name of the dealer, without any decorative text.
// Add a space character to the output so you can read the name back.
toStream << D.m_strName_Dealer << " ";
// Do the same for name of the agent and address.
toStream << D.m_strAgent << " ";
toStream << D.m_strAddress << " ";
// Write the number of items in Stock first.
// This will be necessary when reading the data.
toStream << D.Stock.size() << " ";
// Now write the Stock items, which spaces between each field you write.
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first << " " << it->second.first << " " << it->second.second << " ";
}
return toStream;
}
std::istream &operator>>(std::istream &fromStream, CDealer &D)
{
// Read the name of the dealer.
fromStream >> D.m_strName_Dealer;
// Read the name of the agent.
fromStream >> D.m_strAgent;
// Read the address.
fromStream >> D.m_strAddress;
// Read the number of items.
size_t num;
fromStream >> num;
// Now read the Stock items.
for ( size_t i = 0; i < num; ++i )
{
// Obtained the types for key, val1, and val2 from
// the error message posted in a comment by OP.
CStock key;
int val1;
double val2;
fromStream >> key >> val1 >> valu2;
// Add the item to Stock.
D.Stock.insert(std::make_pair(key, std::make_pair(val1, val2)));
}
return fromStream
}
Problem
The problem can be reduced to the following case:
#include <map>
int main(void) {
std::map<int, int> val;
auto it = val.begin();
it->first = 10;
}
The key values in a std::map are const (the pair of key and value is defined as std::pair<const Key, T>), so for
map<CStock, pair<unsigned, double>>
we have a map containing pairs of
std::pair<const CStock, pair<unsigned, double>>
which makes it->first in
fromStream >> it->first >> it->second.first >> it->second.second;
a const CStock and results in the reported error.
This all makes sense because the map is ordered by the key, so if you could change the key whenever you wanted to whatever you wanted the map would soon be incoherent.
Documentation on std::map
Solution
There is no simple solution. You cannot do this. You must either make the items and then place them in the map or use one of the emplace family of functions to construct them directly in the map. Once they are in the map you can change the value, but not the key. You could remove the item, and thus the key, and reinsert with a different key, but why are you doing this in the first place? I recommend a rethink of your logic.

C++ compile error; stream operator overload

I'm learning C++ stream operator overloading. Can't get this to compile in Visual Studio.
In the istream& operator section, the compiler highlights the carats just after ins and says no operator >> matches these operands.
Can someone quick run it and tell me what's wrong?
*****************
// CoutCinOverload.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
using namespace std;
class TestClass {
friend istream& operator >> (istream& ins, const TestClass& inObj);
friend ostream& operator << (ostream& outs, const TestClass& inObj);
public:
TestClass();
TestClass(int v1, int v2);
void showData();
void output(ostream& outs);
private:
int variable1;
int variable2;
};
int main()
{
TestClass obj1(1, 3), obj2 ;
cout << "Enter the two variables for obj2: " << endl;
cin >> obj2; // uses >> overload
cout << "obj1 values:" << endl;
obj1.showData();
obj1.output(cout);
cout << "obj1 from overloaded carats: " << obj1 << endl;
cout << "obj2 values:" << endl;
obj2.showData();
obj2.output(cout);
cout << "obj2 from overloaded carats: " << obj2 << endl;
char hold;
cin >> hold;
return 0;
}
TestClass::TestClass() : variable1(0), variable2(0)
{
}
TestClass::TestClass(int v1, int v2)
{
variable1 = v1;
variable2 = v2;
}
void TestClass::showData()
{
cout << "variable1 is " << variable1 << endl;
cout << "variable2 is " << variable2 << endl;
}
istream& operator >> (istream& ins, const TestClass& inObj)
{
ins >> inObj.variable1 >> inObj.variable2;
return ins;
}
ostream& operator << (ostream& outs, const TestClass& inObj)
{
outs << "var1=" << inObj.variable1 << " var2=" << inObj.variable2 << endl;
return outs;
}
void TestClass::output(ostream& outs)
{
outs << "var1 and var2 are " << variable1 << " " << variable2 << endl;
}
operator >>() should take TestClass& instead of const TestClass& as its 2nd parameter since you are expected to modify that parameter while reading from istream.
You should change the parameter type of inObj to reference to non-const, since it's supposed to be modified in operator>>. You can't modify on a const object, so you can't call opeartor>> on const object (and its members), that's what compiler complains.
friend istream& operator >> (istream& ins, TestClass& inObj);
Remove the qualifier const
friend istream& operator >> (istream& ins, const TestClass& inObj);
^^^^^
You may not change a constant object.

C++ error: no match for 'operator>>' in 'input >> Group1->Entrepreneur::Item'|

I'm getting this error and have searched for a good 4-6 hour to try and get a solution but none of my searches yielded any results in my particular situation so here is my main.cpp.
#include <iostream>
#include <string>
#include "Entrepreneur.h"
#include <fstream>
using namespace std;
bool operator >(const Entrepreneur & Group1,const Entrepreneur & Group2)
{
if((Group1.Points > Group2.Points || Group1.Points == Group2.Points) && Group1.Profit > Group2.Profit )
{
return true;
}
else
{
return false;
};
};
ostream &operator<<( ostream &output,const Entrepreneur &Group1)
{
output <<"\nItem : " << Group1.Item << "\nNr : " << Group1.Nr << "\nDonation : R" << Group1.Donation << "\nStart up amount : R" << Group1.StartUpAmt << "\nExpenses : R" << Group1.Expenses <<"\nPoints : " << Group1.Points << "\nSold : " << Group1.Sold << endl;;
return output;
};
istream &operator>>( istream &input,const Entrepreneur & Group1)
{
cout << "Enter Items to be sold,Donation amount,number of members & startup amount. Seperated by a space." << endl;
input >> Group1.Item >> Group1.Donation >> Group1.Nr >> Group1.StartUpAmt;
return input;
};
int main()
{
return 0;
};
and here is my header file.
#ifndef ENTREPRENEUR_H_INCLUDED
#define ENTREPRENEUR_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
class Entrepreneur{
private:
string Item;
int Nr;
double Donation;
double StartUpAmt;
double Expenses;
double Income;
double Profit;
int Points;
bool Sold;
public:
Entrepreneur(){
Item = "Not Decided";
Nr = 1;
Donation = 0;
StartUpAmt = 0;
Expenses = 0;
Income = 0;
Profit = 0;
Points = 0;
Sold = 0;
};
void CreatGroup(string iItem,int iNr,double iDonation,double iStartUpAmt){
Item = iItem;
Nr = iNr;
Donation = iDonation;
StartUpAmt = iStartUpAmt;
};
void DisplayGroup(){
cout << "\nItem : " << Item << "\nNr : " << Nr << "\nDonation : R" << Donation << "\nStart up amount : R" << StartUpAmt << "\nExpenses : R" << Expenses << "\nIncome : R" << Income << "\nProfit : R" << Profit << "\nPoints : " << Points << "\nSold : " << Sold << endl;
};
void set_info(double iExpenses,double iIncome,bool iSold){
Expenses = iExpenses;
Income = iIncome;
Sold = iSold;
};
void calc_profit(double iDonation,double iStartUpAmt,double iExpenses,double iIncome){
Donation = iDonation;
StartUpAmt = iStartUpAmt;
Expenses = iExpenses;
Income = iIncome;
Profit = Income + (StartUpAmt + Donation) - Expenses;
};
void update_points(){
Points = 0;
if(Nr < 3)
{
Points ++;
}
else
{
Points + 2;
}
if(Donation == 0)
{
Points ++;
}
if(Sold == 1)
{
Points ++;
}
};
void Display(){
cout << "Congratulations to all groups that partook in the challenge !" << endl;
};
friend bool operator >(const Entrepreneur & Group1,const Entrepreneur & Group2);
friend ostream &operator<<( ostream &output,const Entrepreneur & Group1);
friend istream &operator>>( istream &input,const Entrepreneur & Group1);
};
#endif // ENTREPRENEUR_H_INCLUDED
So the error comes from the friend member that overloads the operator>>
In Entrepreneur class in Entrepreneur.h
friend istream &operator>>( istream &input,const Entrepreneur & Group1);
In main.cpp:
istream &operator>>( istream &input,const Entrepreneur & Group1)
{
cout << "Enter Items to be sold,Donation amount,number of members & startup amount. Seperated by a space." << endl;
input >> Group1.Item >> Group1.Donation >> Group1.Nr >> Group1.StartUpAmt;
return input;
};
I have hit a wall here normally 10min on google and I would have fixed it but this one has got the better of me. PS the program is not finished found this when i started testing.
Thanks in advance.
You are trying to read into a const object
istream &operator>>(istream &input, const Entrepreneur & Group1)
This is why your
input >> Group1.Item >> Group1.Donation >> Group1.Nr >> Group1.StartUpAmt;
does not compile. Get rid of the const in the parameter declaration.
An unrelated issue
Points + 2;
This is no-op statement.

C++ operator overload doesnt work

I have a question about operators and how to overload them. There is an example of code and I'm overloading operator<< but it doesn't work. There is class that I use:
class CStudent{ //class for students and their attributes
int m_id;
int m_age;
float m_studyAverage;
public:
CStudent(int initId, int initAge, float initStudyAverage): m_id(initId), m_age(initAge), m_studyAverage(initStudyAverage){}
int changeId(int newId){
m_id = newId;
return m_id;
}
int increaseAge(){
m_age++;
return m_age;
}
float changeStudyAverage(float value){
m_studyAverage += value;
return m_studyAverage;
}
void printDetails(){
cout << m_id << endl;
cout << m_age << endl;
cout << m_studyAverage << endl;
}
friend ostream operator<< (ostream stream, const CStudent student);
};
Overload:
ostream operator<< (ostream stream, const CStudent student){
stream << student.m_id << endl;
stream << student.m_age << endl;
stream << student.m_studyAverage << endl;
return stream;
}
And there is main method:
int main(){
CStudent peter(1564212,20,1.1);
CStudent carl(154624,24,2.6);
cout << "Before the change" << endl;
peter.printDetails();
cout << carl;
peter.increaseAge();
peter.changeStudyAverage(0.3);
carl.changeId(221783);
carl.changeStudyAverage(-1.1);
cout << "After the change" << endl;
peter.printDetails();
cout << carl;
return 0;
}
Where is the problem?
The problem here is you need to learn what references are and the difference between std::ostream and std::ostream& is.
std::ostream& operator<< (std::ostream& stream, const CStudent& student)