Implementing a non-member IO operator - c++

In my assignment I was asked to create the Product class, and I have finished all the implementations except the "non-member IO operator". The question I found it very vague, it asks me to overload the << and >> operators to work with ostream and istream to read a Product from and print a Product to the console in order to make this main function work.
Here I see the main function has cout or cin to Product's derived class SItem, I wonder how I should implement the << >> operators to make the main work.
My main:
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "Product.h"
#include <fstream>
#ifdef TAB
# undef TAB
#endif
#define TAB '\t'
using namespace std;
namespace sict {
class SItem :public Product {
public:
SItem(const char* theSku, const char * theName) :Product(theSku, theName) {}
SItem() {}
virtual std::fstream& store(std::fstream& file, bool addNewLine = true)const {
if (!isEmpty()) {
file.open("ms4.txt", ios::out | ios::app);
file << sku() << TAB << name() << TAB << quantity() << TAB << qtyNeeded() << TAB
<< int(taxed()) << TAB << price() << endl;
file.clear();
file.close();
}
return file;
}
virtual std::fstream& load(std::fstream& file) {
file.open("ms4.txt", ios::in);
char buf[2000];
double dbuf;
int ibuf;
file >> buf;
sku(buf);
file >> buf;
name(buf);
file >> ibuf;
quantity(ibuf);
file >> ibuf;
qtyNeeded(ibuf);
file >> ibuf;
taxed(ibuf != 0);
file >> dbuf;
price(dbuf);
file.clear();
file.close();
return file;
}
virtual std::ostream& write(std::ostream& os, bool linear)const {
return isEmpty() ? os : (os << sku() << ": " << name() << ", qty: "
<< quantity() << ", qtyNeeded:" << qtyNeeded()
<< ", Cost: " << fixed << setprecision(2) << cost());
}
virtual std::istream& read(std::istream& is) {
char buf[2000];
double dbuf;
int ibuf;
cout << "Sku: ";
is >> buf;
sku(buf);
cout << "Name (no spaces): ";
is >> buf;
name(buf);
cout << "Qty: ";
is >> ibuf;
quantity(ibuf);
cout << "Qty Needed: ";
is >> ibuf;
qtyNeeded(ibuf);
cout << "Is taxed? (1/0): ";
is >> ibuf;
taxed(ibuf != 0);
cout << "Price: ";
is >> dbuf;
price(dbuf);
return is;
}
};
}
void dumpFile(fstream& f) {
f.open("ms4.txt", ios::in);
char ch;
while (!f.get(ch).fail()) {
cout.put(ch);
}
f.clear();
f.close();
}
using namespace sict;
void test() {
double res, val = 0.0;
fstream F("ms4.txt", ios::out);
F.close();
SItem S;
SItem T;
SItem U;
cout << "Enter Product info: " << endl;
cin >> S;
SItem V = S;
S.store(F);
T.load(F);
cout << "T: (store, load)" << endl;
cout << T << endl;
cout << "S: " << endl;
cout << S << endl;
cout << "V(S): " << endl;
cout << V << endl;
cout << "U=T & op= :" << endl;
U = T;
cout << U << endl;
cout << "Operator == :" << endl;
cout << "op== is " << (T == "1234" ? "OK" : "NOT OK") << endl;
cout << "op+=: " << endl;
U += 10;
cout << U << endl;
cout << "op+=double : " << endl;
res = val += U;
cout << res << "=" << val << endl;
}
int main() {
fstream F("ms4.txt", ios::out);
F.close();
SItem S;
SItem U("4321", "Rice");
cout << "Empty Prouduct:" << endl << S << endl;
cout << "U(\"4321\", \"Rice\"):" << endl << U << endl;
cout << "Please enter the following information:" << endl;
cout << "Sku: 1234" << endl;
cout << "Name(no spaces) : Blanket" << endl;
cout << "Qty : 12" << endl;
cout << "Qty Needed : 23" << endl;
cout << "Is taxed ? (1 / 0) : 1" << endl;
cout << "Price : 12.34" << endl;
test();
cout << "Please enter the following information:" << endl;
cout << "Sku: 1234" << endl;
cout << "Name(no spaces) : Jacket" << endl;
cout << "Qty : 12" << endl;
cout << "Qty Needed : 23" << endl;
cout << "Is taxed ? (1 / 0) : 0" << endl;
cout << "Price : 12.34" << endl;
test();
dumpFile(F);
cout << "----The End" << endl;
return 0;
}
This is my Product.h:
namespace sict {
class Product : public Streamable {
char sku_[MAX_SKU_LEN + 1];
char * name_;
double price_;
bool taxed_;
int quantity_;
int qtyNeeded_;
public:
Product();
Product(const char*, const char*, bool = true, double = 0, int = 0);
Product(const Product&);
virtual ~Product();
Product& operator=(const Product&);
//setters
void sku(const char*);
void price(double);
void name(const char*);
void taxed(bool);
void quantity(int);
void qtyNeeded(int);
//getters
const char* sku()const;
double price()const;
const char* name()const ;
bool taxed()const;
int quantity()const;
int qtyNeeded()const;
double cost()const;
bool isEmpty()const;
//member operators
bool operator==(const char*);
int operator+=(int);
int operator-=(int);
};
double operator+=(double, const Product&);
std::ostream& operator<<(std::ostream& ostr, const Product& p);
std::istream& operator >> (std::istream& istr, Product& p);
}
All the functions have been implemented except the last two, which are the IO operators.
Streamable class is an abstract class that has no implementations.

You did this wrong in many ways.
Best approach in your case is do it like that.
First define interfaces for stream operations, for your products:
class IStreamPrintable
{
public:
virtual std::ostream& PrintToStream(std::ostream& outStream) const = 0;
};
class IStreamReadable
{
public:
virtual std::istream& ReadFromStream(std::istream& inputStream) = 0;
};
Secondly define stream operators which will use this interfaces.
std::ostream& operator<<(std::ostream& out, const IStreamPrintable& printObject)
{
return printObject.PrintToStream(out);
}
std::istream& operator>>(std::istream& input, IStreamReadable& readObject)
{
return printObject.ReadFromStream(input);
}
Now you Product can inherit this interfaces:
class Product
: public IStreamPrintable
, public IStreamReadable
{
…
};
You do not have to implement it immediately. You can implement those methods in specific product classes SItem and it will work out of the box.
Your method virtual std::fstream& store(std::fstream& file, bool addNewLine = true) is total mess. You are passing fstream object and opening some specific file on it. This is wrong since you are unable to write multiple objects to single file. Keep there ostream object and do not change is state (do only writing), so you could cascade calls and so you could avoid hard-coding a file name.

Related

Why is my copy constructor being called when I try to add an element to my vector?

I am trying to add a customer to my vector and when I run my program the copy constructor is called. I am doing an assignment where I need a vector of customers and have to be able to add customers, display customers, find customers and load/store the data. Have I created the vector wrong? I am only new to c++ and really unsure about vectors.
Code from Main.cpp:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include "Customer.h"
using namespace std;
void OutputFileStream();
void parseLine(const string& str);
void InputFileStream();
void save(vector<Customer> customers);
void load(vector<Customer>& customers);
void addCustomer(vector<Customer>& vect);
void displayAll(vector<Customer>& customers);
//void printActions();
vector<Customer> customers;
void OutputFileStream()
{
cout << "Creating and writing to file: Customer.txt" << endl;
ofstream outStream("customers.txt"); // write mode (overwrites existing data)
if (outStream.good())
{
int customerID = 150033;
outStream << "This is a line of text.\n";
outStream << "This is another line of text.\n";
outStream << "This is a line of text.\n";
int numOfPurchases = 4;
int purchases = 0;
outStream << customerID << "Mr" << "Jack" << "New" << numOfPurchases << purchases << endl;
//int *purchases = customers[0].getPurchases();
outStream.close(); // close file
cout << "File written.\n" << endl;
}
else
cout << "Unable to open file";
}
void parseLine(const string& str) {
stringstream strStream(str); //create string stream from the string
// int customerID;
string title;
string name;
string type;
//int numOfPurchases;
//int purchases;
string s;
int customerID = 150033;
getline(strStream, s, ';');
customerID = stoi(s);
getline(strStream, title, ';');
getline(strStream, name, ';');
getline(strStream, type, ';');
int numOfPurchases = 4;
getline(strStream, s, ';');
numOfPurchases = stoi(s);
int purchases = 0;
getline(strStream, s, ';');
purchases = stoi(s);
int* purchasesArray = new int[3];
purchasesArray[0] = (purchases & (255 << 16)) >> 16;
purchasesArray[1] = (purchases & (255 << 8)) >> 8;
purchasesArray[2] = purchases & 255;
for (int i = 0; i < 3; i++)
{
cout << purchasesArray[i];
}
cout << " CustomerID: " << customerID << "Title:" << title << " Name: " << name << " Type:" << type << " Number of Purchases: " << numOfPurchases << "Purchases: " << purchases << endl;
}
void InputFileStream() {
cout << "Reading from a semi-colon delimited txt file" << endl;
string line;
ifstream inStream("customers.txt"); //opens file as an input file stream
if (inStream.good()) //if the file is opened successfully and not empty
{
while (getline(inStream, line)) //reads line until false return
{
parseLine(line);
}
inStream.close();
}
else
cout << "unable to open file or the file is empty!";
}
void save(vector<Customer> customers)
{
ofstream out("customers.txt");
if(out)
{
for (Customer& c : customers)
{
out << c.save();
}
out.flush();
out.close();
}
else
{
cout << "Error Writing to File" << endl;
}
}
void load(vector<Customer>& customers)
{
ifstream in("customers.txt");
if (in) {
string line;
while (!in.eof())
{
getline(in, line);
if (line != "")
{
Customer c;
c.parse(line);
customers.push_back(c);
}
}
}
}
void addCustomer(vector<Customer>& customers) {
Customer customer;
cin >> customer;
customers.push_back(customer);
}
void displayAll(vector<Customer>& customers)
{
cout << "\nvector contains:\n";
for (Customer c : customers)
cout << c.getCustomerID() << " " << c.getTitle() << c.getName() << c.getNumOfPurchases() << c.getPurchases() << c.getType() << endl;
cout << endl;; //same as calling customer
}
//
//int getCustomerByPurchaseNumber(int numOfPurchases) {
// vector<Customer> customers;
// int pos = -1;
// for (int i = 0; i < customers.size(); i++) {
// if (customers.at(i).getNumOfPurchases() == numOfPurchases) {
// return i;
// }
// }
// return pos;
//}
//
//void findCustomerByPurchaseNumber(vector<Customer>& customers) {
// vector<Customer> customers;
// int numOfPurchases;
// cout << "Please Enter Your Purchase Number:" << endl;
// cin >> numOfPurchases;
// int pos = customers.get(pos);
// getCustomerByPurchaseNumber(numOfPurchases);
// if (pos == -1) {
// cout << "Number of Purchase Not Found! " << endl;
// return customers;
// }
// else {
// cout << "Number Of Purchase Found! " << endl;
//
// return customers* = customers;
// }
//
//}
int main()
{
vector<Customer> customers;
Customer c1 = { 150031, "Mr", "John", 5, 333,362,341, "New" };
customers.push_back(c1);
//InputFileStream();
/* Customer customer;
customer.setCustomerID(150032);
customer.setTitle("Mr");
customer.setName("Joey");
customer.setNumOfPurchases(3);
customer.setPurchases(366, 352, 334);
customer.setType("New");
cout << customer.getCustomerID() << endl;
cout << customer.getTitle() << endl;
cout << customer.getName() << endl;
cout << customer.getNumOfPurchases() << endl;
cout << customer.getPurchases() << endl;
cout << customer.getType() << endl;
*/
return 0;
}
Code from Customer.cpp:
#include "Customer.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include<utility>
using namespace std;
//default constructor
Customer::Customer() {
}
//Full constructor
Customer::Customer(int customerID, string title, string name, int numOfPurchases, int purchase1, int purchase2, int purchase3, string type)
{
this->customerID = customerID;
this->title = title;
this->name = name;
this->numOfPurchases = numOfPurchases;
purchases = new int[3];
purchases[0] = purchase1;
purchases[1] = purchase2;
purchases[2] = purchase3;
this->type = type;
}
Customer::Customer(const Customer& source) //copy constructor
{
cout << "copy constructor called" << endl;
this->customerID = source.customerID;
this->title = source.title;
this->name = source.name;
this->numOfPurchases = source.numOfPurchases;
this->purchases = new int[3];
purchases[0] = source.purchases[0];
purchases[1] = source.purchases[1];
purchases[2] = source.purchases[2];
this->type = source.type;
}
//overloaded assignment operator=
Customer& Customer::operator= (Customer& otherCustomer)
{
cout << "Overloaded assignment operator= called" << endl;
//self-assignment guard
if (this == &otherCustomer)
return *this; //refernce to the same object
// copy data from the source (rhs) to this object (the destination)
name = otherCustomer.name;
//must make a new scores object to store a copy of the other student
if (purchases != nullptr)
delete[] purchases;
purchases = new int[3];
for (int i = 0; i < 3; i++) {
purchases[i] = otherCustomer.purchases[i];
}
//return this existing object so we can chain this operator
return *this;
}
string Customer::save()
{
stringstream out;
out << this->customerID << ";";
out << this->title << ";";
out << this->name << ";";
out << this->type << ";\n";
out << this->numOfPurchases << ";";
int* purchases = 0;
out.flush();
return out.str();
}
void Customer::parse(string line)
{
stringstream in(line);
string customerIDLine;
getline(in, customerIDLine, ';');
customerID = stoi(customerIDLine);
getline(in, title, ';');
getline(in, name, ';');
getline(in, type, ';');
string numOfPurchases;
getline(in, numOfPurchases, ';');
int s = stoi(numOfPurchases);
int* purchasesArray = new int[3];
purchasesArray[0] = (s & (255 << 16)) >> 16;
purchasesArray[1] = (s & (255 << 8)) >> 8;
purchasesArray[2] = s & 255;
}
void Customer::addCustomer(vector<Customer>& customers )
{
//after read data
int customerID;
cout << "Please Enter Customer ID: " << endl;
cin >> customerID;
string title;
cout << "Please Enter Title: " << endl;
getline(cin, title);
string name;
cout << "Please Enter Name: " << endl;
getline(cin, name);
string type;
cout << "Please Enter Type: " << endl;
getline(cin, type);
int numOfPurchases;
cout << "Please Enter Number of Purchases: " << endl;
cin >> numOfPurchases;
int purchase1;
cout << "Please Enter First Purchase: " << endl;
cin >> purchase1;
int purchase2;
cout << "Please Enter Second Purchase: " << endl;
cin >> purchase2;
int purchase3;
cout << "Please Enter Third Purchase: " << endl;
cin >> purchase3;
Customer c;
customers.push_back(c);
//Customer c();
}
Customer::~Customer() {
cout << "Destructor ~Customer called" << endl;
delete[] purchases;
}
// Overloaded insertion operator (Outputs Character object data as an output stream)
// Defined in header file as a "friend" function, as it is not a member function
//
ostream& operator<<(ostream& out, Customer& customer)
{
cout << "Customer details ( output by insertion operator<< )" << endl;
cout << "Customer ID: " << customer.customerID << endl;
cout << "Title: " << customer.title << endl;
cout << "Name: " << customer.name << endl;
cout << "Number of purchases: " << customer.numOfPurchases << endl;
cout << "Purchases: ";
for (int i = 0; i < 3; i++)
{
if (i > 0) cout << ",";
cout << customer.purchases[i];
}
cout << "Type: " << customer.type << endl;
return out;
}
istream& operator>> (istream& in, Customer& customer)
{
cout << "Enter Customer details ( using the extraction operator>> )" << endl;
cout << "Enter Customer ID: " << endl;
cin >> customer.customerID;
cout << "Enter Title: " << endl;
getline(cin, customer.title);
cout << "Enter Name: " << endl;
getline(cin, customer.name);
cout << "Enter Number of Purchases: ";
cin >> customer.numOfPurchases;
cout << "Enter Purchases: ";
cin >> customer.purchases[0];
cin >> customer.purchases[1];
cin >> customer.purchases[2];
cout << "Enter Type";
getline(cin, customer.type);
cout << endl;
return in;
}
int Customer::getCustomerID()
{
return customerID;
}
string Customer::getTitle()
{
return title;
}
string Customer::getName()
{
return name;
}
int Customer::getNumOfPurchases()
{
return numOfPurchases;
}
int* Customer::getPurchases()
{
return purchases;
}
string Customer::getType()
{
return type;
}
void Customer::setCustomerID(int customerID)
{
if (customerID < 1) {
cout << "Customer ID has to be equal to 1 or more" << endl; //Changed all the "throw invalid_argument" messages to cout as they were causing an issue with my main.cpp file and an abort message kept appearing every time I ran my main.cpp file.
}
this->customerID = customerID;
}
void Customer::setTitle(string title)
{
if (title.length() < 2) {
cout << "Title has to be more than or equal to 2 characters" << endl;
}
this->title = title;
}
void Customer::setName(string name)
{
if (name.length() < 4) {
cout << "Length of name should be more than or equal to 4 characters" << endl;
}
this->name = name;
}
//Got help ith this on stack overflow as I was using "&&" instead of using "||" for the if statement
void Customer::setNumOfPurchases(int numOfPurchases)
{
if(numOfPurchases <0 || numOfPurchases > 10000){
cout << "Number of purchases should be between 0 to 10000" << endl;
}
this->numOfPurchases = numOfPurchases;
}
void Customer::setPurchases(int purchase1, int purchase2, int purchase3)
{
if (purchase1 < 0 || purchase2 < 0 || purchase3 < 0) {
cout << "Purchases must be more than or equal to zero" << endl;
}
}
//Got help from stack overflow on comparing strings as I originally didnt use "type.compare"
void Customer::setType(string type) {
if (type.compare("New") !=0 || type.compare("Either") !=0) {
cout << "Type of purchase has to be New or Either" << endl;
}
}
Code from Customer.h:
#pragma once
#include<iostream>
using namespace std;
#include<string>
#include <vector>
class Customer
{
private:
int customerID;
string title;
string name;
int numOfPurchases;
int* purchases;
string type;
public:
Customer(); // default constructor
Customer(int customerID, string title, string name, int numOfPurchases, int purchase1, int purchase2, int purchase3, string type);
//copy overload assignment
Customer& operator=(Customer& otherCustomer);
Customer(const Customer& source);
string save();
void parse(string line);
void addCustomer(vector<Customer>& customers);
~Customer(); //destructor
//Getters and Setters
void setCustomerID(int customerID);
void setTitle(string title);
void setName(string name);
void setNumOfPurchases(int numOfPurchases);
void setPurchases(int purchase1, int purchase2, int purchase3);
void setType(string type);
int getCustomerID();
string getTitle();
string getName();
int getNumOfPurchases();
int* getPurchases();
string getType();
void printCustomer() {
cout << customerID << "," << title << "," << name << "," << numOfPurchases << "," << purchases << "," << type << endl;
}
friend std::ostream& operator<<(std::ostream& out, Customer& customer); // overloaded operator<<
friend istream& operator>> (istream& in, Customer& customer); // overloaded operator >>
};
Vectors hold objects. If you have an object outside of the vector there is no way to have that same object inside the vector. You can move from one instance to one in the vector (that will move the instances inner parts, but there are still two instances). push_back does make a copy of its parameter when it cannot be moved from.
As you have a constructor that takes all necessary parameters, you can use emplace_back to avoid the copy and construct the instance right in place in the vector:
customers.emplace_back( customerID, title, name, numOfPurchases, purchase1, purchase2, purchase3, type);
If you check reference on std::vector<T,Allocator>::push_back object can be added to vector either by being copied (option1) or being moved (option 2).
So you can create a move constructor for your customer class:
class_name ( class_name && );
And then call push_back using that move consturctor (option 2) on vector:
void push_back( T&& value );
The std::vector<Customer> holds the element by value so just using std::vector::push_back will copy your local object into the vector.
To mitigate this you could either implement move semantics and move the the Customer object like this:
#include <utility>
....
Customer c;
c.parse(line);
customers.push_back(std::move(c));
or construct the object in-place:
// Post c++17 you could use `push_back()` here as well as
// c++17 has mandatory copy elision.
customers.emplace_back(Customer{}); // or customers.emplace_back(); only
customers.back().parse(line);
Both solutions require c++11.
You could also store a reference/pointer and allocate the object on the heap.
std::vector<std::unique_ptr<Customer>> customerVector;
customer_vector.push_back(std::make_unique<Customer>());
// or customer_vector.push_back(std::make_shared<Customer>());
Or
std::vector<Customer*> customerVector;
// Beware for memory leaks, needs to be deleted.
// Basically never use code like this post c++11).
customer_vector.push_back(new Customer{});

Reading and writing from a file

I need to make card catalog entries for a library system and will need a menu and save the card and read the card from a file. I'm just looking for help and not saving for anyone to do it just tips if you have any because I want ro learn and not just copy and paste.
I already finished the information they need to add but having a problem making them save and read from a file and choosing from the menu to add new entry, review entry.
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
struct libary
{
string title;
string author;
string ISBN_code;
int page_count;
int publish_year;
} s[10];
bool processMenu()
{
int choice, spot;
cout << "Main Menu" << endl;
cout << "Select your options" << endl;
cout << "1. Add a new entry to the system" << endl;
cout << "2. Review an entry" << endl;
cout << "3. Save entries to a file" << endl;
cout << "4. Read entries from a file" << endl;
cin >> choice;
switch (choice)
{
case 1:
cout << "which spot do you want to add a entry to the system " << endl;
cin >> spot;
break;
case 2:
cout << "which spot do you want to review to a file " << endl;
cin >> spot;
break;
case 3:
cout << "Save entries to a file" << endl;
ofstream myfile;
myfile.open("file.txt");
myfile << "this will show in the file";
break;
case 4:
cout << "read entries from a file :" << endl;
break;
case 7:
return 0;
}
}
int main()
{
while (!processMenu())
{
cout << "Sorry, that is not a valid choice." << endl;
cout << "Please try again." << endl << endl << endl;
}
cout << " Enter information of each card catalog" << endl;
for (int i = 0; i < 10; i++)
{
cout << endl;
cout << " Enter title " << endl;
cin >> s[i].title;
cout << endl;
cout << " Enter author " << endl;
cin >> s[i].author;
cout << endl;
cout << " Enter Page count " << endl;
cin >> s[i].page_count;
cout << endl;
cout << " Enter publish year " << endl;
cin >> s[i].publish_year;
cout << endl;
cout << " Enter ISBN code, it should be 13 digits" << endl;
cin >> s[i].ISBN_code;
while (s[i].ISBN_code.length() != 13)
{
cout << " Please enter a ISBN code thats 13 digits" << endl;
cin >> s[i].ISBN_code;
cout << endl;
cout << endl;
}
}
cout << " Displaying Information" << endl;
for (int i = 0; i < 10; i++)
{
cout << " title: " << s[i].title << endl;
cout << " author: " << s[i].author << endl;
cout << " page count: " << s[i].page_count << endl;
cout << " publish year: " << s[i].publish_year << endl;
cout << " ISBN code: " << s[i].ISBN_code << endl;
}
return 0;
}
It lets me pick a spot but won't add new entry nor review or save/read.
You can make your own type writable to a stream (cout or ofstream) and/or readable from a stream (cin or ifstream) just by overriding its output (operator<<) and/or input operator (operator>>).
#include <fstream> // ifstream, ofstream
#include <iostream> // cin, cout
#include <string> // getline, string
using namespace std;
struct Card
{
// Writing to the standard output ('cout').
friend ostream& operator<<(ostream& os, const Card& card);
// Reading from the standard input ('cin').
friend istream& operator>>(istream& is, Card& card);
// Writing to a file ('ofstream').
friend ofstream& operator<<(ofstream& os, const Card& card);
// Reading from a file ('ifstream).
friend ifstream& operator>>(ifstream& is, Card& card);
string title = "";
string author = "";
string isbn = "";
int page_count = -1;
int publish_year = -1;
};
ostream& operator<<(ostream& os, const Card& card)
{
os << "Title: " << card.title << '\n'
<< "Author: " << card.author << '\n'
<< "ISBN: " << card.isbn << '\n'
<< "Page count: " << card.page_count << '\n'
<< "Publish year: " << card.publish_year << '\n';
return os;
}
istream& operator>>(istream& is, Card& card)
{
// 'getline' needs for the reading of strings because the title of a book or
// its author's name may contain Space (' ') characters. 'cin' and
// 'ifstream' separate input by every whitespace. So, if we want to allow
// spaces in our fields we have to separate them by a special delimiter
// character. We use the Newline ('\n') character here to separate fields.
// `page_count` and `publish_year` are numbers, they don't contain
// whitespaces, so they simply can be read with 'operator>>'.
cout << "Title: ";
std::getline(is, card.title);
cout << "Author: ";
std::getline(is, card.author);
cout << "ISBN code: ";
std::getline(is, card.isbn);
cout << "Page count: ";
is >> card.page_count;
cout << "Publish year: ";
is >> card.publish_year;
return is;
}
// The next two functions is for files.
ofstream& operator<<(ofstream& os, const Card& card)
{
os << card.title << '\n'
<< card.author << '\n'
<< card.isbn << '\n'
<< card.page_count << '\n'
<< card.publish_year << '\n';
return os;
}
ifstream& operator>>(ifstream& is, Card& card)
{
std::getline(is, card.title);
std::getline(is, card.author);
std::getline(is, card.isbn);
is >> card.page_count;
is >> card.publish_year;
return is;
}
int main()
{
Card card;
// Read the informations of a card from the standard input.
cin >> card;
// Write the informations of a card to the standard output.
cout << card;
// Write the informations of a card to "file.txt".
ofstream ofs("file.txt");
ofs << card;
ofs.close();
// Read the informations of a card from "file.txt".
ifstream ifs("file.txt");
ifs >> card;
ifs.close();
return 0;
}
Important: For the sake of simplicity, I omitted error checking at the opening of a file and at the writing/reading of a stream. In your code you should do these.

Error with pointer when using dynamic_cast to detect derived class

I'd like to have some help on an issue I face.
I made an inheritance with polymorph program with class Shape and Circle (derived from shape). So I have some code like this
main.cpp
Shape* shape = new (nothrow) Shape[size]();
input_circle(shape);
show_circle_area(shape);
and a procedure in main.cpp too
void show_circle_area(Shape *mshape){
int i;
sort(mshape,mshape+totshape,sortByArea);
cout << "CIRCLE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Circle*> (mshape[i]))
cout << mshape[i].getWidth() << " " << mshape[i].getArea() << " " << mshape[i].getPerimeter() << endl;
}
when I run this program, I always got this error :
main2.cpp: In function 'void output_circle(Shape*)':
main2.cpp:66:39: error: cannot dynamic_cast '*(mshape + ((sizetype)(((unsigned int)i) * 40u)))' (of type 'class Shape') to type 'class Circle*' (source is not a pointer)
if (dynamic_cast<Circle*> (mshape[i]))
^
Anyone can help what should I do to fix this?
main.cpp (updated)
#include "Shape.h"
#include "Circle.h"
#include "Rectangle.h"
#include "Square.h"
#include <fstream>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <iomanip>
#include <limits>
#include <typeinfo>
using namespace std;
const int size = 200;
int totshape = 0;
// INPUT FROM FILE
void input_circle(Shape* mshape[]){
ifstream file;
int i;
double r;
file.open("circle.txt");
while (file >> r){
Circle* crl = new (nothrow) Circle(r);
mshape[totshape]=crl;
totshape++;
}
file.close();
}
void input_rectangle(Shape* mshape[]){
ifstream file;
int i;
double w,h;
file.open("rectangle.txt");
while (file >> w >> h){
Rectangle* rec = new (nothrow) Rectangle(w,h);
mshape[totshape]=rec;
totshape++;
}
file.close();
}
void input_square(Shape* mshape[]){
ifstream file;
int i;
double s;
file.open("square.txt");
while (file >> s){
Square* sqr = new (nothrow) Square(s);
mshape[totshape]=sqr;
totshape++;
}
file.close();
}
//OUTPUT TO FILE
void output_circle(Shape *mshape[]){
int i;
ofstream file;
file.open("outcircle.txt");
file << "radius\tarea\tperimeter" << endl;
for (i=0;i<totshape;i++){
if (dynamic_cast<Circle*> (mshape[i]))
file << mshape[i]->getWidth() << "\t" << mshape[i]->getArea() << "\t" << mshape[i]->getPerimeter() << endl;
}
file.close();
}
void output_rectangle(Shape *mshape[]){
int i;
ofstream file;
file.open("outrectangle.txt");
file << "width\theight\tarea\tperimeter" << endl;
for (i=0;i<totshape;i++){
if (dynamic_cast<Rectangle*> (mshape[i]))
file << mshape[i]->getWidth() << "\t" << mshape[i]->getHeight() << "\t" << mshape[i]->getArea() << "\t" << mshape[i]->getPerimeter() << endl;
}
file.close();
}
void output_square(Shape *mshape[]){
int i;
ofstream file;
file.open("outsquare.txt");
file << "sisi\tarea\tperimeter" << endl;
for (i=0;i<totshape;i++){
if (dynamic_cast<Square*> (mshape[i]))
file << mshape[i]->getWidth() << "\t" << mshape[i]->getArea() << "\t" << mshape[i]->getPerimeter() << endl;
}
file.close();
}
//SORTING STL FOR AREA AND PERIMETER
bool sortByArea(Shape lhs[], Shape rhs[]) {
return lhs->getArea() < rhs->getArea();
}
bool sortByPerimeter(Shape lhs[], Shape rhs[]){
return lhs->getArea() < rhs->getArea();
}
//SHOW DATA SORT BY AREA
void show_shape_area(Shape *shape[]){
int i;
sort(shape,shape+totshape,sortByArea);
cout << "ALL SHAPE" << endl;
for (i=0;i<totshape;i++)
cout << shape[i]->getWidth() << " " << shape[i]->getWidth() << " " << shape[i]->getArea() << " " << shape[i]->getPerimeter() << endl;
}
void show_circle_area(Shape *mshape[]){
int i;
sort(mshape,mshape+totshape,sortByArea);
cout << "CIRCLE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Circle*> (mshape[i]))
cout << mshape[i]->getWidth() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
}
void show_rectangle_area(Shape *mshape[]){
int i;
sort(mshape,mshape+totshape,sortByArea);
cout << "RECTANGLE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Rectangle*> (mshape[i]))
cout << mshape[i]->getWidth() << " " << mshape[i]->getHeight() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
}
void show_square_area(Shape *mshape[]){
int i;
sort(mshape,mshape+totshape,sortByArea);
cout << "SQUARE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Square*> (mshape[i]))
cout << mshape[i]->getWidth() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
}
//SHOW DATA SORT BY PERIMETER
void show_shape_perimeter(Shape *shape[]){
int i;
sort(shape,shape+totshape,sortByPerimeter);
cout << "ALL SHAPE" << endl;
for (i=0;i<totshape;i++)
cout << shape[i]->getWidth() << " " << shape[i]->getWidth() << " " << shape[i]->getArea() << " " << shape[i]->getPerimeter() << endl;
}
void show_circle_perimeter(Shape *mshape[]){
int i;
//Shape * tempshape;
sort(mshape,mshape+totshape,sortByPerimeter);
cout << "CIRCLE" << endl;
for (i=0;i<totshape;i++)
//cout << "masuk for" << endl;
//tempshape=&mshape[i];
if (dynamic_cast<Circle*> (mshape[i])){
cout << mshape[i]->getWidth() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
//cout << "masuk" << endl;
}
}
void show_rectangle_perimeter(Shape *mshape[]){
int i;
sort(mshape,mshape+totshape,sortByPerimeter);
cout << "RECTANGLE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Rectangle*> (mshape[i]))
cout << mshape[i]->getWidth() << " " << mshape[i]->getHeight() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
}
void show_square_perimeter(Shape *mshape[]){
int i;
sort(mshape,mshape+totshape,sortByPerimeter);
cout << "SQUARE" << endl;
for (i=0;i<totshape;i++)
if (dynamic_cast<Square*> (mshape[i]))
cout << mshape[i]->getWidth() << " " << mshape[i]->getArea() << " " << mshape[i]->getPerimeter() << endl;
}
//ADD DATA
void add_circle(Shape *mshape[]){
int input;
cout << endl << "Masukkan jari-jari : ";
while (!(cin >> input) || input < 0) // <<< note use of "short circuit" logical operation here
{
cout << "Input tidak valid" << endl;
cout << "Masukkan jari-jari : ";
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // NB: preferred method for flushing cin
}
Circle* crl = new (nothrow) Circle(input);
mshape[totshape]=crl;
totshape++;
}
void add_rectangle(Shape *mshape[]){
int inwidth, inheight;
cout << endl << "Masukkan panjang : ";
while (!(cin >> inwidth) || inwidth < 0) // <<< note use of "short circuit" logical operation here
{
cout << "Input tidak valid" << endl;
cout << "Masukkan panjang : ";
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // NB: preferred method for flushing cin
}
cout << endl << "Masukkan lebar : ";
while (!(cin >> inheight) || inheight < 0 || !(inheight < inwidth)) // <<< note use of "short circuit" logical operation here
{
cout << "Input tidak valid" << endl;
cout << "Masukkan lebar : ";
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // NB: preferred method for flushing cin
}
Rectangle* rec = new (nothrow) Rectangle(inwidth,inheight);
mshape[totshape]=rec;
totshape++;
}
void add_square(Shape *mshape[]){
int input;
cout << endl << "Masukkan sisi : ";
while (!(cin >> input) || input < 0) // <<< note use of "short circuit" logical operation here
{
cout << "Input tidak valid" << endl;
cout << "Masukkan sisi : ";
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // NB: preferred method for flushing cin
}
Square* sqr = new (nothrow) Square(input);
mshape[totshape]=sqr;
totshape++;
}
//DELETE DATA
//MAIN PROGRAM
int main(){
Shape* shape[size];
input_circle(shape);
input_rectangle(shape);
input_square(shape);
show_shape_area(shape);
show_shape_perimeter(shape);
show_circle_area(shape);
show_circle_perimeter(shape);
show_rectangle_area(shape);
show_rectangle_perimeter(shape);
show_square_area(shape);
show_square_perimeter(shape);
//add_circle(shape);
//show_circle_area(shape);
//add_rectangle(shape);
//show_rectangle_area(shape);
//add_square(shape);
//show_square_area(shape);
output_circle(shape);
output_rectangle(shape);
output_square(shape);
return 0;
}
Your problem is here:
mshape[totshape]=crl;
This assignment just copies the "Shape part" inside crl to mshape[totshape] thus mshape[totshape] is still a shape, not a circle.
In order to fix your problem, please use an array of Shape* pointers instead of Shape values:
Shape* shape[size]; // we should write this as size is a const
And, your input_***() functions:
void input_circle(Shape* mshape[]){
ifstream file;
int i;
double r;
file.open("circle.txt");
while (file >> r){
Circle* crl = new Circle(r);
mshape[totshape]=crl;
totshape++;
}
file.close();
}
Note that the function prototype is changed, please do it for other functions, and use mshape[i]->foo instead of mshape[i].foo
The casting now is: if (dynamic_cast<Circle*> (mshape[i])){
Don't forget to free memory before ending since we are using pointers:
for (int i = 0; i < totshape; i++) delete mshape[i];
This deleting would force you to make Shape's destructor be virtual:
class Shape {
public:
virtual ~Shape() {...}
}
Otherwise, Circle, Rectangle ... 's destructors will not be called.

Debug Assertion Failed String!=NULL C++

I've created a class patient that contains some information about patients such as Name, Age, Adress etc.
When I try to compile the program I get an
Debug Assertion Failed (Expression String != NULL) and I can't seem to find why is this happening.
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class pacient{
protected:
int colesterol;
int tensiune_arteriala;
char * nume;
char * prenume;
int varsta;
char * adresa;
char * data_analiza;
public:
//constructor cu toti parametrii
pacient(){};
pacient(int colesterol, int tensiune_art, char * nume, char *prenume,
int varsta, char * adresa, char *data_analiza)
{
(*this).colesterol = colesterol;
tensiune_arteriala = tensiune_art;
strcpy((*this).nume, nume);
strcpy((*this).prenume, prenume);
(*this).varsta = varsta;
strcpy((*this).adresa, adresa);
strcpy((*this).data_analiza, data_analiza);
}
//destructor
~pacient(){
colesterol = 0;
tensiune_arteriala = 0;
strcpy(nume, "");
strcpy(prenume, "");
varsta = 0;
strcpy(adresa, "");
strcpy(data_analiza, "");
}
//functie virtuala
virtual char * get_name() { return "Este pacient"; }
//citirea si afisarea obiectelor
friend istream &operator>> (istream&in, pacient&ob);
friend ostream &operator<< (ostream&out, const pacient&ob);
};
istream& operator>>(istream &in, pacient &ob){
cout << "Nume: "; gets(ob.nume); cout << endl;
cout << "Prenume: "; gets(ob.prenume); cout << endl;
cout << "Varsta: "; cin >> ob.varsta; cout << endl;
cout << "Adresa: "; gets(ob.adresa); cout << endl;
cout << "Data analiza: "; gets(ob.data_analiza); cout << endl;
cout << "Colesterol: "; cin>>ob.colesterol; cout << endl;
cout << "Tensiune arteriala: "; cin>>ob.tensiune_arteriala; cout << endl;
return in;
}
ostream &operator<< (ostream&out, const pacient&ob){
cout << "Nume: "; puts(ob.nume); cout << endl;
cout << "Prenume: "; puts(ob.prenume); cout << endl;
cout << "Varsta: "; cout << ob.varsta; cout << endl;
cout << "Adresa: "; puts(ob.adresa); cout << endl;
cout << "Data analiza: "; puts(ob.data_analiza); cout << endl;
cout << "Colesterol: "; cout << ob.colesterol; cout << endl;
cout << "Tensiune arteriala: "; cout << ob.tensiune_arteriala; cout << endl;
return out;
}
int main()
{
pacient a;
cin >> a;
cout << a;
}
That's because you have members that are pointers:
char * nume;
char * prenume;
...
But you don't initalize them, and don't allocate memory, but netvertheless try to use them:
pacient(int colesterol, int tensiune_art, char * nume, char *prenume,
int varsta, char * adresa, char *data_analiza)
{
...
strcpy((*this).nume, nume); ///<======= OUCH !!!!
Consider using string instead of char*:
you won't have to manage memory, it's automatic
you can use = instead of strcpy
you can return string without having to worry about leaking memory

Why I cannot access dynamic allocated memory in my for loop?

I new a memory for my child class type stock which is inherited from base class instrument, when I try to access the second element of my array, it throws error. Things are fine when I my new array size is 1
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Instrument{
public:
virtual void display(){}
virtual void output(){}
virtual void readFile(){}
virtual ~Instrument(){}
};
class Stock :
public Instrument{
public:
Stock(){
}
virtual void input(){
cout << "This is stock, please input its information: ";
cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
}
virtual void display(){
cout <<"This is to display stock: "<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Stock.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a stock: "
<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Stock.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Stock(){}
private:
char name[13];
double bidPrice;
double askPrice;
double lastPrice;
int issueExchange;
};
int main(){
const int N = 5;//it works fine if I use N=1;
Instrument *pBase = NULL;
pBase = new Stock[N];
for (int i = 0; i < N; i++){
pBase[i].input();// here throws an exception and ends the program
pBase[i].display();
pBase[i].output();
}
pBase[N - 1].readFile();
delete[] pBase;
system("pause");
return 0;
}
Polymorphism and pointer arithmetic do not mix, because the arrangement of objects within an array depends on the most-derived size, and polymorphism loses that information. The dynamic allocation is a red herring, you can see the same problem with:
Derived array[2];
Base* p = array;
printf("%p\n", &array[0]);
printf("%p\n", p);
printf("%p\n", &array[1]);
printf("%p\n", p + 1);
printf("%z\n", sizeof (array[0]));
printf("%z\n", sizeof (*p));
Note that the pointer values using array are moving forward by sizeof (Derived), but pointer arithmetic using p is moving forward by sizeof (Base) and not finding the real objects.
Generally you would fix this using an array of Base*, instead of a single Base* combined with pointer arithmetic.
Base* pp[2];
for( auto& elem : array ) pp[&elem - array] = &elem;
printf("%p\n", &array[1]);
printf("%p\n", pp[1]);
// use (*pp[1]) or pp[1]->whatever
Another option is to use an object that remembers the original type:
Derived* allocated = new Derived[N];
std::function<Base& (int)> poly = [allocated](int i){ return allocated[i]; };
and use poly(i) instead of p[i]
But warning, you CANNOT do delete [] &poly(0); because delete[] is not polymorphic either.
Using std::unique_ptr<Derived[]> and std::bind, one could arrange for automatic deallocation when the accessor object finally goes out of scope.
Although Mr.Ben's method is absolutely right, but I totally feel that his C++ and my C++ are not the same language, his is mixing some strange things here, thus according to his idea, I tried to modify my code like this.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Instrument{
public:
virtual void display() = 0;
virtual void output() = 0;
virtual void readFile() = 0;
virtual ~Instrument(){};
};
class Stock :
public Instrument{
public:
Stock(){
cout << "This is stock, please input its information: ";
cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
}
virtual void display(){
cout << "This is to display stock: " << name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Stock.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a stock: "
<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Stock.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Stock(){}
private:
string name;
double bidPrice;
double askPrice;
double lastPrice;
int issueExchange;
};
class Option :
public Instrument{
public:
Option(){
cout << "This is option, please input its information: ";
cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> exp;
}
virtual void display(){
cout << "This is to display option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< exp << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Option.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is an option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< exp << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Option.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Option(){}
private:
string name;
string uname;
double bidPrice;
double askPrice;
double lastPrice;
int contractSize;
double exp;
};
class Future :
public Instrument{
public:
Future(){
cout << "This is option, please input its information: ";
cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> tickSize >> contractMonth;
}
virtual void display(){
cout << "This is to display option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< tickSize << " "
<< contractMonth << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Future.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a future: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< tickSize << " "
<< contractMonth << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Future.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Future(){}
private:
string name;
string uname;
double bidPrice;
double askPrice;
double lastPrice;
int contractSize;
int tickSize;
int contractMonth;
};
int main(){
int N = 20;
//shared_ptr<Instrument> pBase[N];
vector<shared_ptr<Instrument>> pBase(N);
int i = 5;
for (i = 0; i < N; i++) pBase[i] = make_shared<Stock>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
for (i = 0; i < N; i++) pBase[i] = make_shared<Option>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
for (i = 0; i < N; i++) pBase[i] = make_shared<Future>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
system("pause");
return 0;
}
tl;dr answer: don't convert subclass array (Stock[N]) to a base class pointer (pBase). Either directly use Stock*, or create an array of pointers instead:
auto arr = new Stock*[N];
for (int i = 0; i < N; i++) {
arr[i] = new Stock();
}
// unrelated but suggested: C++11 unique_ptr is recommended:
vector<unique_ptr<Stock>> v(N);
Detailed reasons:
1) Array bracket operator is a syntactic sugar: a[b] -> *(a + b);
2) Pointer arithmetic is not polymorphic, it's always based on static types:
pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Instrument) * i);
3) This is, however, what you want:
pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Stock) * i);
4) As long as sizeof(Instrument) != sizeof(Stock), you are in trouble.