Creating a class while using cascading and PLP - c++

So I am trying to create a class ( Version ) with data members. The member functions are setters ( allow for cascading calls ) and getters ( use PLP and handle constant objects ). In the int main, it allows the user to input the numbers, then it needs to use cascading member functions calls for all data members in one statement and display the version by calling the getters and have them return just the value of the data member.
I pretty much coded everything, but I feel like I'm missing a step or doing something wrong with the parameters. My error says that I need a ')' for my setters, but I feel like it is a different problem. I did not get all my notes for how to do this, so can someone tell me what I am missing or need to fix? Thanks!
#include<iostream>
using namespace std;
class Version
{
private:
int major, minor, service_pack;
public:
Version();
Version& setMajor(int maj);
Version& setMinor(int min);
Version& setService_pack(int sp);
int getMinor(Version *const this);
int getMajor(Version* const this);
int getService_pack(Version* const this);
};
Version::Version()
{
major = 0;
minor = 0;
service_pack = 0;
}
Version& Version::setMinor(int min)
{
minor = min;
return *this;
}
Version& Version::setMinor(int maj)
{
major = maj;
return *this;
}
Version& Version::setMinor(int sp)
{
service_pack = sp;
return *this;
}
int Version::getMinor(Version* const this)
{
return this->minor;
(*this).minor;
}
int Version::getMajor(Version* const this)
{
return this->major;
(*this).major;
}
int Version::getService_pack(Version* const this)
{
return this->service_pack;
(*this).service_pack;
}
int main()
{
int minor, major, service_pack;
Version a;
cout << "Enter minor, major, and service pack: " << endl;
cin >> minor, major, service_pack;
a.setMinor(minor).setMajor(major).setService_pack(service_pack);
cout << "Major: " << a.getMajor() << "Minor: "<< a.getMinor << "Service Pack: " << a.getService_pack();
system("PAUSE");
}

You are missing parenbtheses in main() for diplaying a.getMinor(). Without the parentheses, you'd refer to the member function itself.
Your getters should in principle take no arguments if you return the value. In any case, avoid passing arguments called this.
One further improvement you could make, would be to declare the getters as const:
int Version::getMinor() const
{
return minor; // this-> is only needed to disambiguate
}

Related

Accessor Method to view private variable based on argument in a class in c++?

My problem is that I have many variables in my class and I want them to be accessed via an accessor method. Of course I could have several accessor functions to output my private variables but how can I make it so I can access any of them via an argument. My class:
class Character {
public:
void setAttr(string Sname,int Shealth,int SattackLevel,int SdefenseLevel) {
name = Sname;
health = Shealth;
attackLevel = SattackLevel;
defenseLevel = SdefenseLevel;
}
int outputInt(string whatToOutput) {
return whatToOutput //I want this to either be health, attackLevel or defenseLevel
}
private:
string name;
int health;
int attackLevel;
int defenseLevel;
};
Basically what I want to know is how do I return a private variable in regards to the outputInt function. Most OOP tutorials have one function to return each variable which seems like a very unhealthy thing to do in a large program.
C++ doesn't support what you try to accomplish: reflection or detailed runtime information about objects. There is something called "Run-Time Type Information" in C++, but it can't provide information about your variable name: the reason is because, in the compiled and linked binary this information (names of your variables) will not be necessarily present anymore.
However, you can accomplish something close to that, using i.e. std::unordered_map instead of plain integer variables. So it's possible to access values by their names, as strings.
Please consider the following code:
#include <string>
#include <iostream>
#include <unordered_map>
using namespace std;
class Character {
public:
void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
name = Sname;
values.insert(std::make_pair("health", Shealth));
values.insert(std::make_pair("attackLevel", SattackLevel));
values.insert(std::make_pair("defenseLevel", SdefenseLevel));
}
int outputInt(const string& whatToOutput) {
return values.at(whatToOutput);
}
private:
string name;
std::unordered_map<std::string, int> values;
};
int main(int argc, char* argv[]) {
Character yourCharacter;
yourCharacter.setAttr("yourName", 10, 100, 1000);
std::cout << "Health: " << yourCharacter.outputInt("health") <<std::endl;
std::cout << "Attack level: " << yourCharacter.outputInt("attackLevel") << std::endl;
std::cout << "Defense level: " << yourCharacter.outputInt("defenseLevel") << std::endl;
return 0;
}
It will output as expected:
Health: 10
Attack level: 100
Defense level: 1000
Another option without dependency on unordered_map would be, to use predefined static strings for your variable names and an array or vector for your values. So we could replace the class Character above with something like:
static std::string variableNames[3] = {
"health",
"attackLevel",
"defenseLevel"
};
class Character {
public:
void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
name = Sname;
variableValues[0] = Shealth;
variableValues[1] = SattackLevel;
variableValues[2] = SdefenseLevel;
}
int outputInt(const string& whatToOutput) {
int retVal = 0;
for (size_t i = 0; i < sizeof(variableNames)/sizeof(std::string); ++i) {
if (!whatToOutput.compare(variableNames[i])) {
retVal = variableValues[i];
}
}
return retVal;
}
private:
string name;
int variableValues[3];
};
And getting still same output. However, here you have to manage a list with all your variable names inside the string array manually - I don't like this solution and would prefer one of the others above personally.
Most common ways in C++ to handle such a design is to have seperate getHealth(), getAttackLevel(), getDefenseLevel() functions instead. However, this will miss one use-case, which is: if you want to let the user input a string, like i.e. "health" and display the corresponding variable then, you would need to write code by yourself to call the corresponding getXXX() function. If this is not a issue in your case, consider the following code which is much cleaner:
#include <string>
#include <iostream>
using namespace std;
class Character {
public:
void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
name = Sname;
health = Shealth;
attackLevel = SattackLevel;
defenseLevel = SdefenseLevel;
}
int getHealth() const { return health; }
int getAttackLevel() const { return attackLevel; }
int getDefenseLevel() const { return defenseLevel; }
private:
string name;
int health, attackLevel, defenseLevel;
};
int main(int argc, char* argv[]) {
Character yourCharacter;
yourCharacter.setAttr("yourName", 10, 100, 1000);
std::cout << "Health: " << yourCharacter.getHealth() <<std::endl;
std::cout << "Attack level: " << yourCharacter.getAttackLevel() << std::endl;
std::cout << "Defense level: " << yourCharacter.getDefenseLevel() << std::endl;
return 0;
}
One other unrelated advice: Instead of using string as parameter types for your functions, use const string& (const reference to string; see my example code above). This allows easier calling of your functions (they can be called directly with an string literal without the need to create additional variables in the calling code) and they will not make a additional unnecessary copy. The only copy then will take place at: name = Sname; (in your code two copies took place).
I don't know if it can be a good idea for you, but you can use a public typedef struct that you pass by reference and set your value.
class Character {
public:
//...
typedef struct allvalues{
string vname;
int vhealth;
int vattackLevel;
int vdefenseLevel;
}allvalues;
void getValues(allvalues& val){
val.vname = name;
val.vhealth = health;
val.vattackLevel = attackLevel;
val.vdefenseLevel = detenseLevel;
}
//...
};
//...
//somewhere in the code
Character myCarac;
//...
//Here how to use it
Character::allvalues values;
myCarac.getValues(values);

Confused about using constructurs with istream parameters

I have a slight problem when trying to learn how to create constructors.
I am currently learning c++ using the "C++ Primer" book and I've come to a point where I am told to create some constructors then change a code using these constructors. The exercise states that I should rewrite this program using the istream constructor but I don't know how to do this.
int main()
{
Sales_data total;
if (read(cin,total))
{
Sales_data trans;
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
The problem I have is, I have no idea how I am supposed to use an constructor using the istream, i thought it would be simple and just pass in cin as a default value but it does not work. from visual studios I get a "LNK2019" error and from code::blocks "undefined reference to Sales_data::read(std::istream&, Sales_data&)
My code in my header file looks like this:
struct Sales_data
{
Sales_data() = default;
Sales_data(const std::string &s) : bookNo(s){}
Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n){}
Sales_data(std::istream &is)
{
read(is, *this);
}
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
double avg_price() const;
Sales_data add(Sales_data&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
plus some definitions below.
and my cpp file looks like this:
int main()
{
Sales_data total(cin); //results in error "LNK2019" or "undefined reference to Sales_data::read(std::istream&, Sales_data&)"
if (1)
{ //not really sure what to use here but if I get my default value to work I might figure it out.
// I'm thinking it should work with just cin >> total or read(total)
Sales_data trans(cin); //results in error "LNK2019" or "undefined reference to Sales_data::read(std::istream&, Sales_data&)"
while (1)
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
I hope you understand my problem, and I appreciate all the help you provide! :)
It sounds to me as if you're either missing some code from your book, or the book is expecting you to implement other functions and not just the constructor.
The linker error is telling you that it's unable to find an implementation of your read function, which should look like:
std::istream& Sales_data::read(std::istream&, Sales_data&)
{
// TODO - implementation here.
}
It's also worth mentioning that function implementations should be added to the source (.cpp) file.

How to call struct from separate function?

#include <iostream>
#include <cmath>
using namespace std;
struct workers{
int ID;
string name;
string lastname;
int date;
};
bool check_ID(workers *people, workers &guy);
void check_something(workers *people, workers &guy, int& i);
int main()
{
workers people[5];
for(int i = 0; i < 5; i++){
cin >> people[i].ID;
cin >> people[i].name;
cin >> people[i].lastname;
cin >> people[i].date;
if(check_ID(people, people[i]) == true)
cout << "True" << endl;
else
cout << "False" << endl;
check_something(people, people[i], i);
}
return 0;
}
bool check_ID(workers *people, workers &guy){
for(int i = 0; i < 5; i++){
if(people[i].ID == guy.ID)
return true;
break;
}
return false;
}
void check_something(workers *people, workers &guy, int& i){
check_ID(people, guy[i]);
}
This is the code I have, it's not very good example, but I quickly wrote it to represent my problem I have because my project is kinda too big. So basically, I want to call struct from a different function and I'm getting this error:
error: no match for 'operator[]' in guy[i] on this line :
check_ID(people, guy[i]); in the function check_something.
In main, people is an array. You access the ith element of it people[i] and try to pass it to check_something in the position of function-local variable guy. You then try to dereference guy - which is not an array, but a single object instance.
int main()
{
workers people[5]; // <-- array
...
check_something(people /* <-- people */, people[i] /* <-- guy */, i /* <-- i */);
vs
void check_something(workers *people, workers &guy, int& i){
check_ID(people, guy[i] /* <-- array access on single instance*/);
You actually passed the array in the first argument, people. You don't need "guy" here, because it is people[i], isn't it? So you could do:
void check_something(workers *people, int& i){
worker& guy = people[i];
check_ID(people, guy);
or just
void check_something(workers *people, int& i){
check_ID(people, people[i]);
or
would work, or you could just pass
void check_something(workers* people, workers& guy) {
check_id(people, guy);
}
---- EDIT ----
You also have a python-like bug in your check_ID function.
if(people[i].ID == guy.ID)
return true;
break;
In Python, this says:
if people[i].ID == guy.ID:
return True
break
What you want is
if ( people[i].ID == guy.ID ) {
return true;
break;
}
or just
if ( people[i].ID == guy.ID )
return true;
(since the return is going to exit the function, there's no point in also saying break afterwards)
workers does not have an overloaded subscript operator, nor is guy an array. Therefore you cannot call [] on it. Either create an array or delete [i] after guy.
check_ID(people, guy); //delete [i]
Guy is a reference, not a pointer. You cannot use the operator[] on a reference.

Aggregation using C++

I am trying to make one class work with another class. It is supposed to decrement the member of the other class.
my first class is
class Bike
{
private:
int miles;
Speedometer speedom;
static int fuelCount;
public:
Bike();
Bike(int, Speedometer*); //Problem occurs here
~Bike();
int getMiles();
int getFuelCount();
void incrementMiles();
};
int Bike::fuelCount = 0;
Bike::Bike()
{
miles = 0;
fuelCount++;
}
Bike::Bike(int m, Speedometer * spm) //This is where I am having problems
{
miles = m;
speedom = &spm;
}
Bike::~Bike()
{
cout << "The Bike's destructor is running." << endl;
fuelCount--;
}
int Bike::getMiles()
{
return miles;
}
int Bike::getFuelCount()
{
return fuelCount;
}
void Bike::incrementMiles()
{
miles++;
if (miles == 999999)
miles = 0;
}
The other class which is supposed to be included in the first is:
Class Speedometer
{
private:
int fuel;
public:
Speedometer();
Speedometer(int);
~Speedometer();
int getFuel();
void incrementFuel();
void decrementFuel();
};
Speedometer::Speedometer()
{
fuel = 0;
}
Speedometer::Speedometer(int f)
{
fuel = f;
}
int Speedometer::getFuel()
{
return fuel;
}
void Speedometer::incrementFuel()
{
if (fuel <= 15)
fuel++;
}
void Speedometer::decrementFuel()
{
if (fuel > 0)
fuel--;
}
They are supposed to work together. Bike is to be able to work with speedometer object. It should decrease the speedometers current amount of fuel by one gallon for every 24 miles traveled.
This is supposed to be a aggregate relationship not composition.
Please help me just understand how to make that relationship and how its supposed to be called.
Thank you in advance.
here is my main function
btw - i have all the right #includes i just have not listed them here
int main(int argc, char *argv[])
{
Speedometer a(999970, spd);
for(int count = 0; count <=24; count++)
a.decrementMiles();
while (a.getFuel() > 0)
{
a.incrementMiles();
cout<< "Miles:" << a.getMiles() << endl;
cout<< "Fuel:" << a.getFuel() << endl;
}
return 0;
}
You have a large number of issues here.
First of all, in your main(), you construct your Speedometer object with a constructor you have not implemented. The only constructors you have defined are the default constructor and Speedometer(int). You then call Speedometer(int, ???), the ??? being spd because you do not declare spd anywhere in the code you have provided, so we have no idea what it is.
It's really impossible to say what's wrong with your code in its current state.
As written, you've made a composition; Speedometer is part of Bike since it is a field. To make it an aggregation, make Bike hold a pointer to Speedometer. Note that as a consequence, you'll probably need Bike to create or obtain an initial Speedometer (could be NULL to begin with, or pass one in the constructor), and you might want to add accessor methods to Bike in order to add/remove/change the Speedometer.
[edit] Bike might also need to know how to dispose of the Speedometer properly in order to avoid leaking it.
[edit 2] Also as #cjm571 pointed out, your main function is creating and operating directly upon a "disembodied" Speedometer. Shouldn't it be on a Bike? :)
#include <iostream>
using namespace std;
class Bike
{
private:
int miles;
static int fuelCount;
// Speedometer speedom;
public:
Bike();
Bike(int); // Speedometer *); check comment on line 82
~Bike();
int getMiles();
int getFuelCount();
void incrementMiles();
};
int Bike::fuelCount = 0;
Bike::Bike()
{
miles = 0;
fuelCount++;
}
Bike::Bike(int m)//Speedometer (*spm) I don't see the purpose of this in the current state of the program, I may not be seing the whole picture
{
miles = m;
/* speedom = spm; remember, there must be a parent and a child class, at the current state you'r trying
to call a child from parent, the child class has not been defined, so i switched them and now Bike is a chiled. */
}
Bike::~Bike()
{
cout << "The Bike's destructor is running." << endl;
fuelCount--;
}
int Bike::getMiles()
{
return miles;
}
int Bike::getFuelCount()
{
return fuelCount;
}
void Bike::incrementMiles()
{
miles++;
if (miles == 999)
miles = 0;
}
class Speedometer
{
private:
int fuel;
public:
Speedometer();
Speedometer(int f);
int getFuel();
Bike theBike; // This is what you needed in order to make incrementMiles to work.
void incrementFuel();
void decrementFuel();
};
Speedometer::Speedometer()
{
fuel = 0;
}
Speedometer::Speedometer(int f)
{
fuel = f;
}
int Speedometer::getFuel()
{
return fuel;
}
void Speedometer::incrementFuel()
{
if (fuel <= 15)
fuel++;
}
void Speedometer::decrementFuel()
{
if (fuel > 0)
fuel--;
}
int main(int argc, char *argv[])
{
Speedometer a(999); //You never declared this, did you mean spm???
for(int count = 0; count <=24; count++)
a.theBike.incrementMiles();
while (a.getFuel() > 0)
{
a.theBike.incrementMiles();
cout<< "Miles:" << a.theBike.getMiles() << endl;
cout<< "Fuel:" << a.getFuel() << endl;
}
cin.get();
return 0;
} //There is no break declared (that i can see at least) so the program runs an infinite loop
// Don't want to add too many things to it, I don't know what your plan is.
// Hoping to have made it clearer.

I/O overloading and reading from text files

I need to define a read and a print function for a class that has an array of objects as a private variable. I have to read in objects from a text file and print them to the screen. To do this I need to overload the << and >> operators. I understand I need to use loops to read and print the information stored in the array but I'm not sure how to accomplish this. My lecturer has given us a skeleton code which is basically function prototypes and the main function which I need to stick to. I understand how this works with public structs as I have done this exact scenario using that but the private variables of class' are tripping me up.
class EmployeeList {
public:
//Constructors
EmployeeList();
EmployeeList(istream&);
//Accessors
bool isEmpty() const;
bool isFull() const;
int size() const; //Number of employees in list
Employee item(int i) const; //i'th employee
//Mutators
void setItem(int i,const Employee& e);
//I/O functions, sets the i'th emplyee to e
void read(istream&);
void print(ostream&) const;
private:
enum {MAXSIZE = 100};
Employee list[MAXSIZE];
int count; //Number of employees in the current list
};
EmployeeList::EmployeeList() {
count = 0;
}
EmployeeList::EmployeeList(istream& in) {
//list[MAXSIZE] = in;
}
bool EmployeeList::isEmpty() const {
return (count == 0);
}
bool EmployeeList::isFull() const {
return (count == MAXSIZE);
}
int EmployeeList::size() const {
return count;
}
Employee EmployeeList::item(int i) const {
}
void EmployeeList::setItem(int i, const Employee& e) {
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
void EmployeeList::print(ostream& out) const {
for (int i=0; i < size(); i++) {
}
cout << out;
}
The above part is the Class EmployeeList while the below part are overloading functions. The commented out parts are ideas that I thought might work but didn't.
istream& operator>>(istream& in, EmployeeList& l) {
l.read(in);
return in;
}
ostream& operator<<(ostream& out, const EmployeeList& l) {
l.print(out);
return out;
}
Below is the main function given to us.
int main() {
authorInfo();
ifstream infile("a1in.txt");
if(!infile) {
cout << "file 'alin.txt' not found.";
return EXIT_FAILURE;
}
EmployeeList theList(infile);
cout << endl;
cout << theList.size() << " employees read:\n" << theList << endl;
process(theList);
return EXIT_SUCCESS;
}
Hope someone can steer me in the right direction! Let me know if you need more of the code. Thanks!
EDIT:
Employee read and print functions:
void Employee::read(istream& in) {
in >> name >> id >> salary;
}
void Employee::print(ostream& out) const {
out << getName() <<" "<< getID() <<" "<< getSalary() << endl;
}
Employee overloading:
istream& operator>>(istream& in, Employee& e) {
e.read(in);
return in;
}
ostream& operator<<(ostream& out, const Employee& e) {
e.print(out);
return out;
}
EDIT 2: Updated read() function. The line with the while is where the error is.
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee && count < MAXSIZE) {
list[count] = inEmployee;
count++;
}
}
EDIT 3: Here is the print() function I have so far. It does indeed print but I get the default constructor information rather than information from the file. Is this a read or print function issue? I'm thinking read function still.
void EmployeeList::print(ostream& out) const {
cout << endl;
for (int i=0; i < count; i++) {
out << list[count];
}
}
Array Bounds
In your class, you have:
Employee list[MAXSIZE];
Given this, there is an error the code you tried:
EmployeeList::EmployeeList(istream& in) {
list[MAXSIZE] = in;
}
list only has elements from list[0] to list[MAXSIZE - 1]. list[MAXSIZE] is one past the end of the array, and is invalid.
Constructors
That said, I'd strongly recommend against having a constructor that takes an istream&. It is far better to construct an empty object with the default constructor, then use its read(istream&) method (via operator <<) to load the data. In other words, rather than:
EmployeeList theList(infile);
use:
EmployeeList theList;
infile >> theList;
If you're required to have a constructor that takes an istream&, just have it call read() after initializing the object:
EmployeeList::EmployeeList(istream& in): count(0) {
read(in);
}
Note that only one constructor is called, so the initialization in EmployeeList::EmployeeList() does not happen in EmployeeList::EmployeeList(istream&). I hear the new version of C++ deals with this unnecessary repetition, but for the time being that's where we are.
Naming
Another thing: your code will be less confusing with better variable names. In this case:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
Don't say tempList because it's not a "temporary list", it's a single Employee that has been read. Better would be:
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee) {
list[count++] = inEmployee;
}
}
This looks like a homework so i'll try to just give you a hint:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
//here you are creating a tempList so after you fill in the values in tempList
//the tempList is to become a part of Employee list[MAXSIZE];
}
}
and how do you fill in the values? You do this using your constructor and maintaining the count
EmployeeList::EmployeeList(istream& in) {
//here...
}
You could start off by figuring out how to read in input. The approach, which is likely incomplete, that I would take is this:
EmployeeList::EmployeeList(istream& in) {
count = 0;
read(in); // delegate it to avoid duplication
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList && count < MAXSIZE) {
list[count] = tempList;
++count;
}
}
You will need to overload operator>> for Employee class for this to work.
Here is how I would write this, without the skeleton constraint. Feel free to adapt to your assignment requirements.
Source: http://www.ideone.com/R9EeF
Iostreams are hard to master. You have to read about std::getline, the std::ios flags and stringstreams to understand how to parse an employee list with them.
I prefer giving you a working template (that you cannot use for your assignment since I don't make use of the skeleton at all), since there is a lot to say about iostreams.
Also feel free to ask questions, so that I can enhance my answer with your actual problems.