C++ Overloading input with abstract class - c++

I am trying to build a "candy shop user interface" and I have 4 classes:
SweetItem class - abstract base class
Candy, Cookie, IceCream classes - derived from the base class virtually
Cookielida - derived from both cookie and ice cream classes
Now I have in the base class:
class SweetItem
{
public:
/*=====Builders & Destructor & = Operator=====*/
SweetItem();
SweetItem(const SweetItem&);
~SweetItem();
const SweetItem& operator=(const SweetItem&);
/*=====Input\Output operators=====*/
friend ostream& operator<<(ostream&, const SweetItem&);
friend istream& operator>>(istream&, SweetItem&);
/*=====Static members=====*/
static void NoSweets() { cout << num_sweets; }
static int get_num() { return num_sweets; }
/*=====Virtual Interface=====*/
virtual SweetItem* clone() const = 0;
virtual SweetItem* enter() = 0;
virtual void print() const = 0;
virtual int get_amount() const =0;
virtual float get_price() const = 0;
virtual String get_type() const = 0;
virtual float get_total() const = 0;
/*=====Cyber Function=====*/
void color(const int) const;
protected:
private:
static int num_sweets;
};
The input/output are calling virtual functions "enter()" and "print()":
ostream& operator<<(ostream& op, const SweetItem& input) {
if(&input)
input.print();
return op;
}
istream& operator>>(istream& ip, SweetItem& input) {
input.enter();
return ip;
}
When I try to use the "enter()" of the cookielida like this:
int main() {
SweetItem* temp = new Cookielida;
cin >> *temp;
cout << *temp << endl;
}
It prints a default object that i set up instead of the entered selection by user. This is the virtual implementation:
SweetItem* Cookielida::enter() {
String ct, it;
float cp, ip;
cout << "Please select from the available options: " << endl;
cout << "1) Butter cookies " << BUTTERP << "$ per unit" << endl;
cout << "2) Chocolate chip cookies " << CHIPP << "$ per unit" << endl;
cout << "3) Oreo cookies " << OREOP << "$ per unit" << endl;
int opt1;
opt1 = error(1, 3);
switch (opt1)
{
case BUTTER: ct = "Butter cookies";
cp = BUTTERP;
break;
case CHIP: ct = "Chocolate chip cookies";
cp = CHIPP;
break;
case OREO: ct = "Oreo cookies";
cp = OREOP;
break;
default:
break;
}
cout << "Please select from the available options: " << endl;
cout << "1) Vanilla icecream " << VANIP << "$ per unit" << endl;
cout << "2) Chocolate icecream " << CHOCP << "$ per unit" << endl;
cout << "3) Yogurt icecream " << YOGUP << "$ per unit" << endl;
int opt2;
opt2 = error(1, 3);
switch (opt2)
{
case VANI: it = "Vanilla icecream";
ip = VANIP;
break;
case CHOC: it = "Chocolate icecream";
ip = CHOCP;
break;
case YOGU: it = "Yogurt icecream";
ip = YOGUP;
break;
default:
break;
}
cout << "How many cookielidas do you want? " << endl;
int a;
a = error(0, MAXAMOUNT);
SweetItem* temp = new Cookielida(a, ip, cp, it, ct);
return temp;
}
I believe that the problem is that the temp cookielida created in the "enter()" function is destroyed instead of copied to the temp in the "main()".
Also these are the constructors:
Cookielida::Cookielida(int a=0, float ip=0, float cp=0, const String& it="", const String& ct="") :
IceCream(ICEA, ip, it), Cookie(COKA, cp, ct), amount(a), price((ip + cp * 2)*1.5), type(ct + " " + it)
{
}
Cookielida::Cookielida(const Cookielida& input) :
IceCream(ICEA, input.get_ip(), input.get_it()),
Cookie(COKA, input.get_cp(), input.get_ct()),
amount(input.amount), price(input.price), type(input.type)
{
}
This is the output:

The problem is in your enter() function. It doesn't change the object itself, but rather creates a new one which is returned via pointer. So when you call input.enter(); in your operator>> overload, the class input refers to is not varied -- and in fact, nothing happens as the pointer you return is not used at all.
As a workaround, you can do the following
Cookielida& Cookielida::enter() //better return the derived object in a covariant way,
//not the base class pointer
//you can still downcast it if required
{
//read in those many parameters
//then either set the class member variables "a, ip, cp, it, ct"
//directly (--the preferred way)
//or use this cheap alternative
operator=(Cookielida(a, ip, cp, it, ct));
return *this;
}

You just totaly missed the point in design of your Cookielida::enter function. It returns totaly new object and does not change the old one.
Putting it is inside operator>> is just a nonsense, you may actualy want to put it other way arround.
int main() {
SweetItem* temp = new Cookielida;
SweetItem* temp2 = temp->enter();
temp2->print();
delete temp;
delete temp2;
}

Related

Access element of vector

I have a class Student:
class Student {
private:
unsigned int id;
string name;
vector<int> grades;
public:
Student(unsigned int id, string name, vector<int> grades);;
virtual ~Student() {}
unsigned int getId() { return this->id; }
string getName() { return this->name; }
int getGradesAmount() { return this->grades.size(); }
vector<int> getGrades() { return this->grades; }
int getGrade(int i) { return this->grades[i]; }
unsigned int getCoef()
{
unsigned int coef = 1;
for (int i = 0; i < this->grades.size(); i++) { coef *= this->grades[i]; }
return coef;
}
int getNameCoef() { return this->getName().size() % 2; }
ostringstream getInfo()
{
ostringstream info;
info << "ID: " << getId() << ".\n";
info << "Name: " << getName() << ".\n";
info << "Amount of grades: " << getGradesAmount() << ".\n";
info << "Grades:";
for (int i = 0; i < getGradesAmount(); i++)
info << " " << getGrade(i);
info << "\nProduct of grades: " << getCoef() << ".\n";
info << "Is surname has odd number of symbols (0 = no / 1 = yes): " << getNameCoef() << ".\n";
return info;
}
};
Student::Student(unsigned int id, string name, vector<int> grades)
{
this->id = id; this->name = name; this->grades = grades;
}
And a class Group:
class Group : public Student {
protected:
int size = 0;
vector<Student> group;
public:
Group() : Student(getId(), getName(), getGrades()) {}
void addStudent(Student student)
{
if (student.getNameCoef() == 1)
{
if (this->group.size() > 0)
{
for (int i = 0; i < this->group.size(); i++)
{
if (student.getCoef() > group[i].getCoef())
{
this->group.insert(this->group.begin() + i, student);
this->size = this->size + 1;
return;
}
}
}
cout << "\nAdded to start";
this->group.push_back(student);
this->size = this->size + 1;
}
}
};
In Group I'm trying to overload << to make a cout << group.
So, I have added this into a Group:
friend ostream& operator<<(ostream& out, const Group& group) { // overloaded operator of output
out << "\nThere are " << group.size << " students in the group.\n";
for (int i = 0; i < group.size; i++)
{
out << "Student # " << i + 1 << ":\n";
out << group[i].getInfo();
}
return out;
}
But I have this error:
error C2676: binary '[': 'const Group' does not define this operator or a conversion to a type acceptable to the predefined operator
So, I googled for any [] overloaded operators for vector but didn't find anything that work for me. I also tried copying constructor, but it didn't helped me.
How to use group[i].getInfo() ? Or maybe there are some oter ways to access this. So, group[i] must be Student object.
It seems like you are confusing the group which is of type Group with its member, which is confusingly also called group.
Either you provide an Group::operator[] or you change your operator<< to
friend ostream& operator<<(ostream& out, const Group& group) { // overloaded operator of output
auto& vect = group.group;
out << "\nThere are " << vect.size() << " students in the group.\n";
for (int i = 0; i < vect.size(); i++)
{
out << "Student # " << i + 1 << ":\n";
out << vect[i].getInfo();
}
return out;
}
There are several issues with your code (see comments), but the one you're asking about is because of this line:
out << group[i].getInfo();
As JohnFileau mentioned, group is a Group object, not a std::vector. If you want to access the std::vector group in the Group class, you will need group.group:
out << group.group[i].getInfo();
This will fix this issue, but it's only fair to warn you you will soon hit others. I recommend reading the comments on your question and figuring out how to fix them.
Something worth noting here is that part of the reason you're seeing this is you may not realize that operator<<() is not a member function of Group. That is, even if you define it inline like this:
class Group : public Student {
//...
friend ostream& operator<<(ostream& out, const Group& group) { // overloaded operator of output
out << "\nThere are " << group.size << " students in the group.\n";
for (int i = 0; i < group.size; i++)
{
out << "Student # " << i + 1 << ":\n";
out << group[i].getInfo();
}
return out;
}
};
It might look like it's a member of Group, but it's actually not. It is an external friend function. It is not actually a part of Group. That means you can't access the data members directly. For instance,
this->size
will fail because, again, it's not actually a member function.
If you want to access data members of Group, you need to access them via the instance you were passed (i.e., group:
group.size
Note that that means when you do something like group.size, you're actually getting data member Group::size, not the size() from std::vector.

Accessing data from a nested class elegantly

I have the following class:
class BigNum
{
public:
BigNum(string StrNumber) : Number(std::move(StrNumber)) {}
BigNum(const char *StrNumber) : Number(string(StrNumber)) {}
~BigNum() = default;
struct
{
string HEX() { return Number + " - HEX"; }
string DEC() { return Number + " - DEC"; }
string BIN() { return Number + " - BIN"; }
}ToString;
private:
string Number;
};
And in the end I wand to elegantly access functions from that structure in the following way:
BigNum a = "1234";
cout << "a = " << a.ToString.DEC() << endl;
cout << "b = " << a.ToString.HEX() << endl;
The problem here is that I cannot access variable Number from my structure.
I know that something like this would solve my problem:
struct
{
string HEX(BigNum &parent) { return parent.Number + " - HEX"; }
...
}ToString;
The problem with this solution is that it is not comfortable to always pass a pointer to my instance.
What would be a solution in this case to have data in nested class and in the same time to keep calls as simple as a.ToString.DEC()?
In some way you have to give ToString a reference or a pointer to the BigNum object so you can access Number. How about something like this:
class BigNum
{
public:
BigNum(string StrNumber) : Number(std::move(StrNumber)) {}
BigNum(const char* StrNumber) : Number(string(StrNumber)) {}
~BigNum() = default;
// you can make the struct private so the type is not visible externally
struct ToStringType
{
private:
const BigNum& ref;
public:
ToStringType(const BigNum& r) : ref(r) {}
string HEX() { return ref.Number + " - HEX"; }
string DEC() { return ref.Number + " - DEC"; }
string BIN() { return ref.Number + " - BIN"; }
};
ToStringType ToString{ *this };
private:
string Number;
};
Irrelevant, but I would recommend to simply have separate ToStringHex, ToStringDec and ToStringBin functions. Saves on not storing a reference, plus the API is easier this way.
I don't see any rationale in the ToString struct.
Just leave the methods in BIGNUM and you are done.
However, for this specific application (changing the rendering style of your given objet in an ostream) I would let your object to be printed with the typical operator<< overaloading, and then modify the rendering style using io-manipulators, so that you will be able to:
cout << "a (DEC) = " << BigNum::DEC << a << endl;
cout << "a (HEX) = " << BigNum::HEX << a << endl;
A full fledged example:
#include <iostream>
#include <iomanip>
using namespace std;
class BigNum
{
public:
BigNum(string StrNumber) : Number(std::move(StrNumber)) {}
BigNum(const char *StrNumber) : Number(string(StrNumber)) {}
~BigNum() = default;
static std::ios_base& DEC(std::ios_base& os) {
os.iword(rendering_style_xalloc) = 0;
return os;
}
static std::ios_base& HEX(std::ios_base& os) {
os.iword(rendering_style_xalloc) = 1;
return os;
}
static std::ios_base& BIN(std::ios_base& os) {
os.iword(rendering_style_xalloc) = 2;
return os;
}
private:
static int rendering_style_xalloc;
string Number;
friend ostream &operator << (ostream &ostr, const BigNum &bignum);
};
int BigNum::rendering_style_xalloc = std::ios_base::xalloc();
ostream &operator << (ostream &os, const BigNum &bignum) {
switch (os.iword(BigNum::rendering_style_xalloc)) {
case 0:
os << bignum.Number << " - DEC";
break;
case 1:
os << bignum.Number << " - HEX";
break;
case 2:
os << bignum.Number << " - BIN";
break;
default:
os << bignum.Number << " - UNK";
break;
}
return os;
}
int main(int argc, char **argv)
{
BigNum a = "1234";
cout << BigNum::DEC << "a (DEC) = " << a << endl;
cout << BigNum::HEX << "a (HEX) = " << a << endl;
}
References:
https://en.cppreference.com/w/cpp/io/ios_base/iword

List of items, in c++

http://ideone.com/UlHrxS
I made a list of items and i don't know what i did wrong. Please correct me and post the link from ideone. I tried to make a list of game objects in a array but it doesn't work.
Thanks.
#include <iostream>
#include <stdlib.h>
using namespace std;
class item{
private:
public:
item(){
//constructor
}
int id;
};
class sword:public item{
private:
public:
int damage;
string type = "Sword";
};
class potion:public item{
private:
public:
int PlusHealth;
string type = "Potion";
};
class shield:public item{
private:
public:
int armor;
string type = "Shield";
};
int main()
{
item *v[10];
bool run = true;
int aux;
int i = 0;
while(run == true && i<10) {
cout << "1- Sword 2-Shield 3-Potion -- ";
cin >> aux;
switch(aux){
case 1: v[i] = new sword;
cout << "Sword created!\n";
break;
case 2: v[i] = new shield;
cout << "Shield created!\n";
break;
case 3: v[i] = new potion;
cout << "Potion created!\n";
break;
default: run = false;
break;
}
i++;
}
system("cls");
cout << "List of items: \n";
for(int x=0;x=i-1;x++){
cout << v[x]->type;
if(type=="Sword"){
cout << " Damage: " << v[x].damage;
} else if(type=="Shield"){
cout << " Armor: " << v[x].armor;
} else if(type=="Potion") << v[x].PlusHealth;
}
return 0;
}
You are breaking some concepts here.
You have vector of pointers to parent class, item. You should only use the methods and members in the item class.
This is not proper implementation:
for(int x=0;x=i-1;x++){
cout << v[x]->type;
if(type=="Sword"){
cout << " Damage: " << v[x].damage;
} else if(type=="Shield"){
cout << " Armor: " << v[x].armor;
} else if(type=="Potion") << v[x].PlusHealth;
}
Try adding something like this:
class Item
{
public:
// A generic function for child classes to print
// their specific details.
virtual void print_details(std::ostream& out) const = 0;
};
class Sword : public Item
{
public:
void print_details(std::ostream& out) const
{
out << " Damage: " << damage << "\n";
}
};
class Shield : public Item
{
public:
void print_details(std::ostream& out) const
{
out << " Armor: " << armor << "\n";
}
};
class Potion : public Item
{
public:
void print_details(std::ostream& out) const
{
out << " Health: " << PlusHealth << "\n";
}
};
//...
for (unsigned int x = 0; x < v.size(); ++x)
{
cout << "\n"
<< v[x]->type
<< "\n";
// Get the child to print the specifics.
v[x]->print_details(cout);
}
The key point is that you can only access item methods and members directly. You can create item methods, that the child classes will need to implement, for specialized behavior. In the above case, printing specific details.
The item base class contains common methods and members for each child. Keep the concept generic and remember that container of items should be treated generically.
Thanks!, but if i want to access set and get functions of derived classes from the base class ? How i can do that generically? because i have different attributes on every item
Example: v[1].setPlusHealth it works because v[1] is a potion but v[1].setArmor it doesn't work because isn't a armor. How i can do that ?

Inherited overridden method not being called

Why does the depositmoney() method within the operation method of the account class call the depositmoney() of the account class and not the one from the Sav_account class? The direct call to depositmoney() method calls method of the sub-class which is obvious. But can't understand why the indirect call from operations() does not give the expected result.
#include<iostream>
#include<conio.h>
#include<string>
using namespace std;
int temp = 0;
class account
{
protected:
string name;
double balance;
int AccNo;
public:
void operations()
{
int r = 0;
do{
cout << "1.DEPOSIT" << endl;
cout << "2.WITHDRAW" << endl;
cout << "3.CHECK BALANCE" << endl;
cout << "0.EXIT" << endl;
cin >> r;
switch(r)
{
case 1:this->depositmoney();
break;
case 2:this->withdraw();
break;
case 3:this->displaybalance();
break;
case 0:cout << "Exiting" << endl;
break;
default:
break;
}
}
while(r != 0);
}
account(string Name, double bal)
{
name = Name;
balance = bal;
AccNo = temp++;
}
void displaybalance()
{
cout << "name :" << name << endl;
cout << "A/C" << AccNo << endl;
cout << "your balance is " << balance << endl;
}
void depositmoney()
{
float deposit;
cout << "enter deposit" << endl;
cin >> deposit;
balance += deposit;
}
protected:
void withdraw()
{
float withdrawal;
cout << "enter witdrawal amount" << endl;
cin >> withdrawal;
if(balance >= withdrawal)
{
balance -= withdrawal;
}
else
{
cout << "insufficient funds" << endl;
}
}
};
class Sav_account :public account
{
private:
double const CI = 5;
void depositinterest()
{
balance += balance*CI / 100;
}
public:
Sav_account(string Name, double bal) :account(Name, bal)
{
AccType = 0;
}
void depositmoney()
{
account::depositmoney();
depositinterest();
}
};
void main()
{
Sav_account *account1 = new Sav_account("Shaw", 50000);
account1->operations();// DEPOSIT RESULTS IN NO INTEREST
account1->displaybalance();
account1->depositmoney(); // **DEPOSIT FUNCTION OF Sav_account CALLS interest function**
account1->displaybalance();
_getch();
}
Your class is not polymorphic. You have no polymorphic functions and have not overridden anything. (You have hidden functions instead). As such, calling depositmoney(); from a function of account will call account::depositmoney().
To make a class polymorphic you need to use virtual functions. Any function that you want to have polymorphic behaviour needs to be declared as virtual in the base class, e.g. in account here:
virtual void depositmoney() {
In the derived class, since C++11, you can write:
void depositmoney() override
^^^^^^^^
which will cause a compiler error if you accidentally hide a function when you are trying to override it. (Although I'm guessing that you are using a compiler that doesn't support C++11, since most of those will also reject void main).
Also, any polymorphic base class should have a virtual destructor, i.e. virtual ~account() {} otherwise the code account *x = new Sav_account("Shaw", 50000); delete x; would cause undefined behaviour.
void main is illegal, main must have a return type of int.
There aren't any overridden methods in your classes; you haven't made any of them virtual.

Getting "Use of undefined type" and "Must have class/struct/union" errors

EDIT #1: Everything before editing the line
<< "Owner: " << (*wo._owner).getLoginName() << endl;
worked completely fine, or at least didn't throw errors on me.
So I have the following code (obviously there is a lot more that if requested I will post, just not sure if more is needed or that is ok):
class Workout
{
private:
int _workoutid; // the ID of this workout
User* _owner; // Who did this workout
float _distance; // in miles
int _duration; // in seconds
int _day, _month, _year; // date: MM/DD/YYYY
int _weight; // lb, on the date of workout
// private methods (if necessary)
public:
friend std::ostream& operator<< (ostream& out, Workout& wo)
{
out << "Workout ID: " << wo._workoutid << endl
<< "Owner: " << (*wo._owner).getLoginName() << endl
<< "Distance: " << wo._distance << endl
<< "Duration: " << wo._duration / 3600 << ":" << (wo._duration % 3600) / 60 << ":" << wo._duration % 60 << endl
<< "Date: " << wo._month << ":" << wo._day << ":" << wo._year << endl
<< "Weight: " << wo._weight << endl;
return out;
}
// Print workout id, owner’s username, distance
// duration (HH:MM:SS), date (MM:DD:YY) and weight of
// the workout
// and other public methods (mutators/setters, accessors/getters)
Workout(void);
Workout(int, User*, float, int, int, int, int, int);
virtual ~Workout(void);
float getDistance();
void setDistance(float);
};
Workout::Workout(void) : _workoutid(), _distance(), _duration(), _day(), _month(), _year(), _weight()
{
_owner = new User();
}
Workout::Workout(int id, User* user, float distance, int duration, int day, int month, int year, int weight) :
_workoutid(id), _distance(distance), _duration(duration), _day(day), _month(month), _year(year), _weight (weight), _owner(user)
{
}
Workout::~Workout(void)
{
delete [] _owner;
}
class User
{
private:
char* _firstname; // First name
char* _lastname; // Last name
char* _loginname; // Login name
char* _password; // password
Workout* _myWorkouts[50];// an array of pointers to workouts
int _numWorkouts; // Num. of workout logged
User* _buddies[10]; // Friends
int _numBuddies; // Num. of friends
// private methods (if necessary)
public:
friend std::ostream& operator<< (ostream& out, User& user)
{
out << "First Name: [" << user._firstname << "]" << endl
<< "Last Name: ["<< user._lastname << "]" << endl
<< "Login Name: [" << user._loginname << "]" << endl
<< "Number of Workouts: [" << user._numWorkouts << "]" << endl
<< "Number of Friends: [" << user._numBuddies << "]" << endl;
return out;
}
User(void);
User(const char*, const char*, const char*, const char*);
virtual ~User(void);
char* getPassword(void);
char* getLoginName(void);
char* getFirstName(void);
void addWorkout(Workout*);
Workout* getWorkout(int);
void addBuddy(User* buddy);
// and other public methods (mutators/setters, accessors/getters)
};
User::User(void) : _firstname(), _lastname(), _loginname(), _password(),
_myWorkouts(), _numWorkouts(), _buddies(), _numBuddies()
{
}
User::User(const char* first, const char* last, const char* login, const char* pass) : _myWorkouts(), _numWorkouts(), _buddies(), _numBuddies()
{
_firstname = new char[20];
_lastname = new char[20];
_loginname = new char[20];
_password = new char[20];
for (int i=0; i < 20; i++){
_firstname[i] = first[i];
_lastname[i] = last[i];
_loginname[i] = login[i];
_password[i] = pass[i];
}
}
User::~User(void)
{
delete [] _firstname;
delete [] _lastname;
delete [] _loginname;
delete [] _password;
for(int i=0;i<50;i++) delete _myWorkouts[i];
delete [] _myWorkouts;
for(int i=0;i<10;i++) delete _buddies[i];
delete [] _buddies;
//What about variables such as _numWorkouts and _numBuddies?
}
And I am getting the following errors:
Error 1 error C2027: use of undefined type 'User'
Error 2 error C2228: left of '.getLoginName' must have class/struct/union
The first error is because the operator<< method somehow doesn't want to recognize that the (*wo._owner) object of type User initialized (which it is!)
The second error, obviously must be related to the second one, but it doesn't improve my chances of realizing at all how to solve the problem.
If this is indeed the structure of your code then you're trying to use "User" before it's defined.
You can't do this.
Declare your output operator as friend if need be and define it after the definition of User is known.