Overloading operator<< twice in same class for different member variables - c++

Sorry if this question has been asked before, but I'm struggling with overloading the << operator to stream different data into multiple files.
I have a Player class, which has the following attributes:
char* name;
char* password;
int hScore;
int totalGames;
int totalScore;
int avgScore;
I want to overload the << operator twice: one to stream the name, password and hScore to a "Players.txt" file, and a second overload to stream the totalGames, totalScore and avgScore to a different .txt file which is based off each player's name, e.g. "Player1.txt".
Here's what my operator looks like in the Player class:
friend ostream& operator<< (std::ostream& os, Player& player)
{
os << player.name << "\n" << player.encryptPassword((player.password), 3) << "\n" << player.hScore << "\n";
return os;
}
And here's where I am calling it, from a PlayerLibrary class which contains a vector of Players:
ofstream out("Yahtzee.txt");
if (out.is_open())
{
for_each(playerList.begin(), playerList.end(), [&out](Player* player) {out << (*player);});
}
else
{
cout << "THERE WAS AN ERROR WRITING TO FILE\n";
}
Basically, I want to stream the other variables into another file which is named after the player name, and contains a scorecard for each game they've played. So far it looks like:
for (auto it = playerList.begin(); it != playerList.end(); ++it)
{
auto position = it - playerList.begin();
string filename(playerList[position]->getName());
filename = filename + ".txt";
ofstream out2(filename);
for (int i = 0; i < playerList[position]->getNumberOfScorecards(); i++)
{
out2 << *playerList[position]->getScorecard(i);
}
}
This only streams the scorecard and not the totalGames, totalScore and avgScore, like I want it to.
I have tried just moving those variables into the scorecard class, but I feel that it makes more sense to have them where they are.
I understand that I can't overload operator<< twice if both overloads have the same parameters, is there another way of going about this? Is there anyway perhaps in the overloaded function to use the output stream and check the name of the .txt file or something.
Hope the question makes sense.

Rather than defining an operator<< for Player itself, create a couple of utility types that refer to the Player and have their own operator<<s, let them decide which portions of the Player to stream, eg:
class Player
{
private:
std::string name;
std::string password;
int hScore;
int totalGames;
int totalScore;
int avgScore;
...
public:
...
std::string getName{} const { return name; }
...
std::string EncryptPassword(int arg) const { return ...; }
int getNumberOfScorecards() const { return ...; }
Scorecard* getScorecard(int index) const { return ...; }
class Info
{
const Player &m_player;
void print(std::ostream &os) const {
os << m_player.name << "\n" << m_player.encryptPassword(3) << "\n" << m_player.hScore << "\n";
}
public:
Info(const Player &player) : m_player(player) {}
friend std::ostream& operator<<(std::ostream &os, const Info &info)
{
info.print(os);
return os;
}
};
friend class Info;
struct Stats
{
const Player &m_player;
void print(std::ostream &os) const
{
os << m_player.totalGames << "\n" << m_player.totalScore << "\n" << m_player.avgScore << "\n";
}
public:
Stats(const Player &player) : m_player(player) {}
friend std::ostream& operator<<(std::ostream &os, const Stats &stats)
{
stats.print(os);
return os;
}
};
friend class Stats;
};
And then you can use them like this:
ofstream out("Yahtzee.txt");
if (out.is_open())
{
for(auto *player : playerList)
out << Player::Info(*player);
}
else
{
cout << "THERE WAS AN ERROR WRITING TO FILE\n";
}
for (auto *player : playerList)
{
ofstream out2(player->getName() + ".txt");
out2 << Player::Stats(*player);
for (int i = 0; i < player->getNumberOfScorecards(); ++i)
{
out2 << *player->getScorecard(i);
}
}
Online Demo

Related

How can I overload the << operator so I can write the data of an object to a file?

I have an object of type piggyBank and I need to write data of this object into a file and then read it. I am aware of how to write/read to a text file but how can I overload the << operator so I can write data about the object into a file?
My code for the class here:
piggyBank.h
#include <string>
#ifndef PIGGYBANK_H
#define PIGGYBANK_H
class PiggyBank
{
private:
std::string owner;
int balance;
bool broken;
int id;
static int nrOfObjects;
public:
PiggyBank(void);
PiggyBank(std::string name);
std::string getOwnerName() const;
void setOwnerName(std::string name);
bool isBroken() ;
int getBalance(int & amount) ;
};
#endif /* PIGGYBANK_H */
piggyBank.cpp
#include "PiggyBank.h"
#include "readWrite.h"
#include <string>
#include <iostream>
using namespace std;
int PiggyBank::nrOfObjects = 0; // outside constructor
PiggyBank::getNrOfObjects(){
return nrOfObjects;
}
PiggyBank::PiggyBank(void){
{this->owner="";this->balance=0;this->broken=false;}
id = ++nrOfObjects;
}
PiggyBank::PiggyBank(std::string name, int startBalance){
{this->owner=name;this->balance=startBalance;this->broken=false;}
id = ++nrOfObjects;
}
string PiggyBank::getOwnerName() const{
return this->owner;
}
void PiggyBank::setOwnerName(string name){
this->owner = name;
}
bool PiggyBank::isBroken() {
return this->broken;
}
int PiggyBank::getBalance(int & amount) {
if(!broken){
amount = balance;
return 0;
}else{
return -1;
}
}
You want the << operator to be a friend to the class and to return ostream&.
Here is a simple example to get an idea about how it works.
friend ostream& operator << (ostream& os, const PiggyBank& obj)
{
// For example something like this
os << "Information that you want to output to the file:\n";
os << "Owner: " << obj.owner << "\n";
return os;
}
And then you can use it like this:
PiggyBack obj;
ofstream fout("file.txt");
// also check to see if the file opened correctly
if(fout.fail())
{
cout << "File failed to open\n";
return 0;
}
fout << obj;
// now you have written the owner information onto the file as well as the message before it
// inside the operator<< overload
// close the resource at the end
fout.close();
The cool part is that you can use it to print to the console too by changing fout to be cout.
For example:
cout << obj; // will print to the console
Very simple. Overload the inserter operator. Write this into your class:
friend std::ostream& operator << (std::ostream& os, const PiggyBank& pb) {
return os << pb.owner << . . . // Whatever you want
Then you can use the inserter operator as for any other data type:
int main() {
PiggyBank pb;
if (std::ofstream os("FileName"); os) {
os << pb << "\n";
}
return 0;
}

Array based on strings, char list conversions

I create a multiple table of string type. I keep variables inside (int, string). It gives me an error:
[Error] cannot convert 'std::string {aka std::basic_string}' to 'char' in assignment
I've created a tree-shaped suite of functions.The program create a multiple array from a file with this format:
11 10 2001
CSKA Moscow
12 1
Bayern Munich
...
Program:
void llegir(std::fstream &_contingut, std::string *_taula) {
//declaro variables
int dia, mes, any, puntsLocal, puntsVisitant, i = 0;
std::string equipLocal, equipVisitant;
while (!(_contingut.eof())) {
//llegeixo arxiu
_contingut >> dia >> mes >> any; //primera linea
_contingut.ignore();
getline(_contingut, equipLocal); //segona linea
_contingut >> puntsLocal >> puntsVisitant; //tercera linea
_contingut.ignore();
getline(_contingut, equipVisitant); //quarta linea
_taula[i][0] = dia;
_taula[i][1] = mes;
_taula[i][2] = any;
_taula[i][3] = equipLocal.c_str();
_taula[i][4] = puntsLocal;
_taula[i][5] = equipVisitant.c_str();
_taula[i][6] = puntsVisitant;
i++;
}
}
void creartaulaDelFitxer(std::string _fitxer, std::string *_taula, int &n_taula) {
std::fstream arxiu;
arxiu.open(_fitxer, std::fstream:: in );
if (arxiu.is_open()) {
std::cout << "existeix";
} else {
std::cout << "ERROR OBERTURA FITXER";
}
llegir(arxiu, _taula);
}
int main(int argc, char** argv) {
std::string fitxer;
std::string eurolliga[300][7];
int n_taula = 0;
std::cout << "INTRODUEIX NOM FITXER:" << std::endl;
std::cin >> fitxer;
creartaulaDelFitxer(fitxer, *eurolliga, int n_taula);
}
You are mixing pointers, chars and strings which will certainly cause a lot of headache. Try to use the standard containers, like std::string and std::vector. If you need many strings, put them in a vector. When you have a collection of data like
11 10 2001
CSKA Moscow
12 1
Bayern Munich
that describes some entity, create a class for it. You can then add streaming operators for that class to read in one of these entities. If you have a collection of entities, make a container and add streaming operators for that too.
Example:
#include <iostream>
#include <fstream>
#include <vector>
class Game {
std::string equipLocal{};
std::string equipVisitant{};
int dia{}, mes{}, any{};
int puntsLocal{}, puntsVisitant{};
public:
friend std::istream& operator>>(std::istream&, Game&);
friend std::ostream& operator<<(std::ostream&, const Game&);
};
// read one entity from an istream
std::istream& operator>>(std::istream& is, Game& g) {
if(is >> g.dia >> g.mes >> g.any) {
is.ignore();
if(std::getline(is, g.equipLocal) && (is >> g.puntsLocal >> g.puntsVisitant)) {
is.ignore();
std::getline(is, g.equipVisitant);
}
}
return is;
}
// write one entity to an ostream
std::ostream& operator<<(std::ostream& os, const Game& g) {
return os << g.dia << " " << g.mes << " " << g.any << "\n"
<< g.equipLocal << "\n"
<< g.puntsLocal << " " << g.puntsVisitant << "\n"
<< g.equipVisitant << "\n";
}
class EuroLiga {
std::vector<Game> games{};
public:
bool Load(const std::string& filename) {
std::ifstream arxiu(filename);
if(arxiu) {
games.clear();
arxiu >> *this; // use this class' friend, operator>>
return true;
} else
return false;
}
// support for basic non-const iteration over the 'games'
std::vector<Game>::iterator begin() { return games.begin(); }
std::vector<Game>::iterator end() { return games.end(); }
friend std::istream& operator>>(std::istream&, EuroLiga&);
};
// read all entities from an istream
std::istream& operator>>(std::istream& is, EuroLiga& el) {
Game tmp;
while(is >> tmp) {
el.games.push_back(std::move(tmp));
}
return is;
}
int main() {
EuroLiga euroliga;
std::string fitxer;
std::cout << "INTRODUEIX NOM FITXER: ";
std::cin >> fitxer;
euroliga.Load(fitxer);
// display all entities read from the file
for(auto& g : euroliga) {
std::cout << g << "\n";
}
}
void llegir(std::fstream &_contingut, std::string *_taula)
Gets a pointer to a string called _taula, this is probably your array.
However you assign something to your array like this:
_taula[i][0] = dia; // allowed, but bad because char is basically a number.
[...]
_taula[i][3] = equipLocal.c_str(); // not allowed, you are assigning a char pointer to a char.
taula[i] is the i-th string in your array. And by putting [0] you assign to the first character in that string. dia is an integer though.
For example
std::string[] = {"Hello", "world", "I", "am", "alive"};
std::cout << string[1] << std::endl; // output "world"
std::cout << string[1][0] << std::endl; // output 'w'
You can not assign a string to a single character.
As a side note, you should look into declaring an enumeration for your array index (and a constant for it's size) to make it more clear and improve maintainability.
What you should probably be doing is create a struct or class for your, whatever it is
struct whateverItIs {
int dia, mes, any, puntsLocal, puntsVisitant;
std::string equipLocal, equipVisitant;
};
Make a new instance of that in your llegir and push it to the back of a std::vector you get by reference.
Just remember to delete() them later especially before that vector goes out of scope.

Reading from file writing to class member functions and printing on console in C++?

I have 2 classes, I make an object from the second class (I pass the filename as an argument). It should print the contents of the file which contains the following data in the following format:
A.Yordanov 1234567819
S.Todorov 3456789120
D.Lazarov 2569789054
P.Pavlov 4329549823
M.Kalinova 2367892343
B.Georgiev 659045324
I have overloaded the << operator but I when I try to cout << cty2 nothing appears on the console even though I have put the file in the same directory?
// OOP_Homework.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
fstream f;
class CPerson {
string name;
string egn;
public:
CPerson(string n, string e) {
name = n;
egn = e;
}
friend bool operator<(CPerson p1, CPerson p2) {
if (p1.egn < p2.egn) {
return true;
}
return false;
}
friend bool operator==(CPerson p1, CPerson p2) {
if (p1.egn == p2.egn) {
return true;
}
return false;
}
friend ostream& operator<< (ostream o, CPerson pn) {
o << "Egn" << endl;
o << pn.egn << endl;
o << "Name:" << endl;
o << pn.name << endl;
}
friend istream& operator>> (istream is, CPerson p) {
is >> p.egn;
is >> p.name;
return is;
}
string getName() {
return name;
}
string getEgn() {
return egn;
}
void setName(string n) {
name = n;
}
void setEgn(string e) {
egn = e;
}
};
class CCity {
vector<CPerson> pr;
public:
CCity(string fl) {
f.open(fl, ios::out);
string name = "", fn = "";
while (!f.eof()) {
//формата на данните във файла е във вида:
//<име на студент> <факултетен номер>
cin >> name >> fn;
pr.push_back(CPerson(name, fn));
}
f.close();
}
//помощен getter за вектора pr;
vector<CPerson> getV() {
return pr;
}
friend ostream& operator<<(ostream& os, CCity& psn) {
vector<CPerson> ps = psn.getV();
for (auto x = ps.begin(); x != ps.end(); x++) {
os << x->getName() << endl;
os << x->getEgn() << endl;
}
return os;
}
vector<CPerson> getDuplicate() {
set<CPerson> temp;
vector<CPerson> duplicates;
for (auto i = pr.begin(); i != pr.end(); i++) {
for (auto x : pr) {
if (*i == x) {
temp.insert(*i);
}
}
}
for (auto j : temp) {
duplicates.push_back(j);
}
temp.clear();
//връщаме студентите с повтарящи се егн-та
return duplicates;
}
void removeDuplicates() {
sort(pr.begin(),pr.end());
pr.erase(unique(pr.begin(), pr.end()));
}
void removeVector(vector<CPerson> ob) {
for (auto i:ob) {
for (auto x = pr.begin(); x != pr.end(); x++) {
if (i == *x) {
pr.erase(x);
}
}
}
}
};
int main()
{
CCity cty2("persons.txt");
//cout << cty1 << endl;
cout << cty2 << endl;
return 0;
}
The code for your overload for operator<< should be
friend ostream& operator<< (ostream &o, const CPerson &pn) {
// ^ ^^^^^ ^
o << "Egn" << endl;
o << pn.egn << endl;
o << "Name:" << endl;
o << pn.name << endl;
return o; // since return type is ostream&
}
You should be passing the stream by reference and returning it once you've finished using it. Passing by-reference ensures that the same stream is used (unlike pass-by-value).
Another thing to note would be your CPerson p function arguments. These should be passed by-reference as well. With operator<<, your CPerson won't be modified, so we can keep it as const.
You may also want to watch out your operator>>. Again, istream is should be istream &is. With the CPerson p parameter, you will modify it, so you'll definitely want to be passing it as a reference.
friend istream& operator>> (istream &is, CPerson &p) {
// ^ ^
is >> p.egn;
is >> p.name;
return is;
}
In your other overloads, you should be using const CPerson &p as well. Moreover, your overloads for operator< and operator== don't need to be friended since they're not accessing any other class's private members. Thus,
bool operator<(const CPerson &p1, const CPerson &p2) {
if (p1.egn < p2.egn) {
return true;
}
return false;
}
bool operator==(const CPerson &p1, const CPerson &p2) {
if (p1.egn == p2.egn) {
return true;
}
return false;
}
You'll want to get used to passing your classes by-reference as it saves space (not having to copy-construct another instance of the class as passing by-value would do). It's thus, more efficient both space-wise and performance-wise to pass your classes by reference.

C++ Operator overloading for << with abstract class

Here are my header files:
//Frame.h
#pragma once
class Frame {
string frameName;
protected:
double fileSize;
vector<Attribute> attributes;
public:
Frame(string f, double size, vector<Attribute> d) :frameName(f), fileSize(size), attributes(d) {}
virtual ~Frame() {}
string& GetFrameName() { return frameName; }
Attribute& operator[](int);
int size() { return attributes.size(); }
virtual void Compress() = 0;
friend ostream& operator<<(ostream&, Frame&);
};
// AudioFrame
#pragma once
class AudioFrame :public Frame {
static const int RATES = 3;
static constexpr double BITRATE[]{128,160,192};
static constexpr double COMPRESSION_RATIO[]{11.1,9.1,7.1};
public:
AudioFrame(string frameName, double fileSize, vector<Attribute> d) :Frame(frameName,fileSize, d) {}
~AudioFrame(){}
void Compress();
friend ostream& operator<<(ostream&, AudioFrame&);
};
//ImageFrame.h
#pragma once
class ImageFrame :public Frame {
static const int BITS = 8;
static constexpr double COMPRESSION_RATIO = 6.0;
static constexpr double BITDEPTH_FACTOR[] {11.1,4.6,3.5,2.4,1.9,1.5,1.2,1.0};
public:
ImageFrame(string fileName, double fileSize, vector<Attribute> d) :Frame(fileName, fileSize, d) {}
~ImageFrame(){}
void Compress();
friend ostream& operator<<(ostream&, ImageFrame&);
};
Each one of them have a **friend ostream& operator<<(ostream&, ImageFrame&);**
But when i do this
ImageFrame test;
cout << test << endl;
Only Frame class's operator << is called. Any solution to this?
Also, professor doesn't want me to change anything from the header files!
EDIT:
here is how i implement the operator <<:
// code from AudioFrame.cpp
ostream& operator<<(ostream& os, AudioFrame& obj) {
os << "AudioFrame" << endl;
os << "Name = " << obj.GetFrameName() << endl;
for (unsigned int i = 0; i < obj.attributes.size(); i++) {
os << "\tBandwidth #" << i << ": " << obj.attributes[i] << endl;
}
return os;
}
// code from ImageFrame.cpp
ostream& operator<<(ostream& os, ImageFrame& obj) {
os << "ImageFrame" << endl;
os << "Name = " << obj.GetFrameName() << endl;
for (unsigned int i = 0; i < obj.attributes.size(); i++) {
os << "\tResolution #" << i << ": " << obj.attributes[i] << endl;
}
return os;
}
// code from Frame.cpp
ostream& operator<<(ostream& os, Frame& obj) {
return os;
}
But when i run the test code, only the code from Frame.cpp is run.
EDIT2:
So i feel like i should also share my real test code:
int type;
deque<Frame*> frames; // all frames are stored here
// user is promoted a console menu for selecting a frame type (ImageFrame or AudioFrame)
if (type == 1)
frames.push_back(new AudioFrame(...));
else
frames.push_back(new ImageFrame(...));
// now when i need to print all frames i do
for (unsigned int i = 0; i < frames.size(); i++)
cout << *(frames[i]) << endl;
If you REALLY want cout << *(frames[i]) << endl; to dispatch polymorphically, and you REALLY cannot alter the headers, then you have no choice but to rely on dynamic typing.
ostream& operator<<(ostream& stream, Frame& frame) {
AudioFrame* as_audio_frame = dynamic_cast<AudioFrame*>(&frame);
ImageFrame* as_image_frame = dynamic_cast<ImageFrame*>(&frame);
if(as_audio_frame) {
return stream << *as_audio_frame;
}
if(as_image_frame) {
return stream << *as_image_frame;
}
//normal frame code
}
However, this is TERRIBLE code, and signals that some reorganizing is severely needed in the headers. So i suspect there is something wrong in your intrepretation of your assignment.
Edit, well, actually, there is a hilariously overengineered alternative...
class FrameAdapterInterface {
public:
virtual ~FrameAdapterInterface() {};
protected:
virtual std::ostream print(std::ostream&) = 0;
friend ostream& operator<<(ostream&, FrameAdapterInterface &);
};
template<typename FRAME_T>
class AdapatedFrameType : public FrameAdapterInterface {
FRAME_T data_;
public:
template<typename... ARGS_T>
AdapatedFrameType(ARGS_T&&... args)
: data_(std::forward<ARGS_T>(args)...) {}
ostream& print(std::ostream& stream) override {
return stream << data_;
}
};
ostream& FrameAdapterInterface::operator<<(ostream& stream, FrameAdapterInterface& frame) {
return frame.print(stream);
}

No operator << matches these operands [duplicate]

This question already has answers here:
How can I use cout << myclass
(5 answers)
Closed 4 months ago.
I've been reading questions here for an hour or two regarding this error I'm getting and most of them forgot to #include string (which I had already done), or to overload the << operator.
Here's the code in question:
void Student::getCoursesEnrolled(const vector<Course>& c)
{
for (int i = 0; i < c.size(); i++)
{
cout << c[i] << endl;
}
}
And the error I'm getting:
Error: No operator matches these operands
operand types are: std::ostream << const Course
All I'm trying to do is return the vector. I read about overloading the << operator but we haven't learned any of that in class so I'm assuming there is another way of doing it?
I appreciate your time!
All I'm trying to do is return the vector.
Not quite; you're trying to print it using cout. And cout has no idea how to print a Course object, unless you provide an overloaded operator<< to tell it how to do so:
std::ostream& operator<<(std::ostream& out, const Course& course)
{
out << course.getName(); // for example
return out;
}
See the operator overloading bible here on StackOverflow for more information.
The problem is that operator << is not overload for type Course objects of which you are trying to output in statement
cout << c[i] << endl;
You need to overload this operator or write your own function that will output an object of type Course in std::ostream
For example let assume that below is a definition of class Course
class Course
{
private:
std::string name;
unsigned int duration;
public:
Course() : duration( 0 ) {}
Course( const std::string &s, unsigned int n ) : name( s ), duration( n ) {}
std::ostream & out( std::ostream &os ) const
{
return ( os << "Course name = " << name << ", course duration = " << duration );
}
};
When you can write
std::vector<Course> v = { { "A", 1 }, { "B", 2 }, { "C", 3 } };
for ( const Course &c : v ) c.out( std::cout ) << std::endl;
Instead member function out you can overload operator <<. For example
class Course
{
private:
std::string name;
unsigned int duration;
public:
Course() : duration( 0 ) {}
Course( const std::string &s, unsigned int n ) : name( s ), duration( n ) {}
friend std::ostream & operator <<( std::ostream &os, const Course & c )
{
return ( os << "Course name = " << c.name << ", course duration = " << c.duration );
}
};
and use it as
std::vector<Course> v = { { "A", 1 }, { "B", 2 }, { "C", 3 } };
for ( const Course &c : v ) std::cout << c << std::endl;
The stream operator << is used to "output" some representation of that object. If you don't want to overload the operator yet just pick some property to output instead:
for (int i = 0; i < c.size(); i++)
{
cout << c[i].Name << endl; // assuming Name is a property of Course
}
When you DO overload the operator you just decide then what the proper representation of a Course is:
ostream& operator<< (ostream &out, Course &c)
{
out << c.Name "(" << c.Description << ")";
return out;
}
Your Course class needs to implement an operator:
class Course
{
public:
/*
* Your code here
*/
// Probably missing this:
friend std::ostream& operator << (std::ostream& os, const Course& course)
{
os << course.name(); // etc..
return os;
};
}; // eo class Course
Since you haven't yet learned to overload operator<<, what you can do instead is to print each member of your Course class. You haven't posted the definition of Course, but perhaps it's something like this:
class Course
{
public:
int get_number() { return _number; }
const std::string& get_name() { return _name; }
private:
int _number;
std::string _name;
};
then you can say:
void Student::getCoursesEnrolled(const vector<Course>& c)
{
for (int i = 0; i < c.size(); i++)
{
cout << c[i].get_number() << " "
<< c[i].get_name() << std::endl;
}
}
Your problem is this particular part:
cout << c[i]
In your case c[i] is an object of type Course as dvnrrs correctly pointed out. So either:
implement the overloaded << operator for your object OR
if your Course object is in someway a typedef to a primitive try explicitly casting it to a string type (or similar)