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;
}
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;
}
I am trying to take out to the console window my vector of pointers but still doesn't work. I can fill the vector but when I try to cout<< my object noting comes out on the console window.
This is header file with declarations of the private class members and functions:
#pragma once
#include"CDealer.h"
#include<vector>
#include<fstream>
#include<iterator>
class CShop
{
public:
CShop();
~CShop();
CShop(const string &fname);
friend ostream &operator << (ostream &toStream, const CShop &S);
friend istream &operator >> (istream &fromStream, CShop &S);
private:
string m_strNameShop;
string m_strCity;
vector<CDealer*> Dealers;
};
This is my constructor with file from where I bring the data
#include "CShop.h"
CShop::CShop(){}
CShop::~CShop(){}
CShop::CShop(const string &fname)
{
fstream File(fname, ios::in);
if (File.is_open())
{
CDealer c;
File >> m_strNameShop;
File >> m_strCity;
while (File.is_open())
{
File >> c;
Dealers.push_back(new CDealer(c));
}
File.close();
}
else
throw "ERROR! ";
}
and this is overloading operator<<:
ostream &operator << (ostream &toStream, const CShop &S)
{
return toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer *>::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
toStream << "Dealer " << *it++;
}
}
So in the end my main
#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;
Please help Me :)
You have a premature return in the function.
return toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
//^^^
Remove it.
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
Add a return before the end of the function.
std::ostream& operator<<(std::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())
{
toStream << "Dealer " << *it++;
}
return toStream;
}
Suggestions for further improvement.
The line
toStream << "Dealer " << *it++;
will print only the pointer values. If you want to print the details of the object the pointers point to, change it to:
Dealer* dealerPtr = *it++;
toStream << "Dealer " << *dealerPtr;
The following block is not right. File.is_open() will be always true in this block.
while (File.is_open())
{
File >> c;
Dealers.push_back(new CDealer(c));
}
Change it to:
while (File >> c)
{
Dealers.push_back(new CDealer(c));
}
So I've made a class for a location,in which I store it's coordonates and it's time,in utc. I overloaded the >> operator like this
friend ifstream& operator >>(ifstream& in, loc_list& l)
{
char bf[40];
in >> bf;
l.setID(bf);
long t=0;
in >> l.utc;
//l.setTime(t);
double point;
in >> point;
l.p.setX(point);
in >> point;
l.p.setY(point);
in >> l.speed;
return in;
}
and the << operator like this:
friend ostream& operator <<(ostream& out,const loc_list &l)
{
out << l.id << endl;
out << put_time(gmtime(&l.utc),"%c %Z" )<< endl;
//out << l.utc << endl;
out << l.p.getX() << endl;
out << l.p.getY() << endl;
out << l.speed;
return out;
}
however,the gmtime in the << operator works only when I create an object using the constructor. When I read it with cin>> it breaks.
I've debugged the program but the objects contains the right data
print from debugger
so,any thoughts?
The counterpart of put_time is get_time.
I suggest using:
std::tm tm
in >> get_time(&tm, "%c %Z");
and then update l.utc from tm.
l.utc = mktime(&tm);
I stored some strings in a text file using overloaded insertion operator.
ostream & operator << (ostream & obj,Person & p)
{
stringstream ss;
ss << strlen(p.last) << p.last << strlen(p.first) << p.first
<< strlen(p.city) << p.city << strlen(p.state) << p.state;
obj << ss.str();return obj;
}
The contents of file look like this
4bill5gates7seattle10washington
I now need to read the length first and display the string.And continue to display all the strings.How to do this with an overloaded extraction operator?
Read it one character at a time and use std::string::push_back to append to a string variable. There's a std::stoi which will convert your string lengths to an integer. Might I suggest that when you create your text file that you put a whitespace after your integer length, then you can just cin >> string_length and avoid using if statements to control when you've found the end of a number, or the beginning of a new string.
Also, it would be more beneficial, if you showed us what you've attempted, so that we could help you more specifically.
You may do:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
int main() {
std::istringstream in("4bill5gates7seattle10washington");
std::vector<std::string> strings;
unsigned length;
while(in >> length) {
std::string s;
if(in >> std::setw(length) >> s)
strings.push_back(s);
}
for(const auto& s : strings)
std::cout << s << '\n';
}
Disclaimer: The file format is evil.
Note: This does not extract a 'Person', but fields. I leave that to you.
Make operator << like this
ostream & operator >> ( ostream & obj, Person & p )
{
obj << strlen( p.last ) << " " << p.last << " " << strlen( p.first ) << " " << p.first << " "
<< strlen( p.city ) << " " << p.city << " " << strlen( p.state ) << " " << p.state;
return obj;
}
and operator >> like this
istream & operator >> ( istream & obj, Person & p )
{
obj >> p.last >> p.first >> p.city >> p.state;
return obj;
}