adding an array of class objects into one master class object - c++

for a project I had to create a class that had variables for feet and inches, and had a method to add these variables together from object 1 and object 2, then object 3, 4, and so on.
CDistance CDistance::add(const CDistance& yourDist) const
{
CDistance total;
total.feet += yourDist.feet;
total.inches += yourDist.inches;
/*if (total.inches > 12)
{
total.feet += (total.inches / 12);
total.inches = total.inches % 12;
}*/
return total;
}
that's the method I have for adding, and here is a function in the main source file, where I process each class
void printAndAdd(const CDistance distList[], int size)
{
CDistance new_total;
new_total = distList[0].add(distList[1].add(distList[2].add(distList[3].add(distList[4]))));
new_total.printDist();
}
And here is the method I use to print out the data on screen
void CDistance::printDist() const
{
cout << "Feet: " << this->feet << "\n\n";
cout << "Inches: " << this->inches << "\n\n";
}
I thought about using a for loop, for that 2nd line, but I'm having a bit of a problem. Whenever I print the data out, it's 0. As if the add function isn't working, which I'm not quite sure I even understand what I did. From what I think I'm doing, it's creating a new obejct, adding the variables from the referenced to object to the created object, that commented out section is a section I just took out for now and will add later, and then it returns the object. When I call the function in my main source file it would set the object new_total equal to the sum of object 0, 1, 2, 3, and 4. Am I close, or way off to what's actually happening? I should also explain that I've only been programming for about a year, it's really intriguing to me, but naturally difficult at times, and I'm still trying to grasp my hands around the idea of classes in c++.

The problem is that you never use the instance variables when adding. Instead you always start from a freshly minted object. Try this:
CDistance CDistance::add(const CDistance& yourDist) const
{
CDistance total(*this);
total.feet += yourDist.feet;
total.inches += yourDist.inches;
this->feet += yourDist.feet;
this->inches += yourDist.inches;
return total;
}

I've done some playing with your code and it looks like this line should is incorrect:
CDistance total;
The value of total is never initialized and is therefore always going to be whatever your default constructor has defined it to be (probably 0/0). Thus, the result of that call will always end up whatever was passed to the input. I think you meant to do this:
CDistance total = *this;
That will copy the current values for feet an inches into the temp and then the following lines will add the input. Passing through the chain of calls like you do should now concatenate the additions as expected.

Related

I want return type pointer but i want my function to return pointer of derive class

Dragon* Dragon::spawn() {
int x = rand() % 5;
int y;
if (!if_locked(x)) //is a function to see if that id is unlocked because i want some dragon to be generated only if you have certain xp so it will call func again until unlocked id is generated
spawn();
else
y = unlocking(m); // Y is generated form 1-5, I have assigned Id to each derive class whosoever id matches Y that pointer will be returned
if (y == 1) {
GroundDragon* pt;
return pt;
}
if (y == 2) {
WaterDragon* st;
return st;
}
if (y == 3) {
IceDragon*bt;
return bt;
}
if (y == 4) {
FireDragon* ct;
return ct;
}
if (y == 5) {
DarkDragon* dark;
return dark;
}
}
As you can see im making syntax mistakes i hope someone can guide me
The return type of function is base class and all the classes in if statement are derive class
so i can later use this function
template<class T>
void spawner(T*) {// I will spawn() fucntion as perimeter at time of call
T = new T;
}
Forgive me if im repaeting question the last time i post it didnt get attention i was expecting so i modifed my code a bit hopeful it is clear now`
int Dragon::unlocking(Mage m) {
if (m.getxp() <= 50 and m.getxp() <= 100) {
unlock[0] = 1;
cout << "Congratulation GroundDragon unlocked " << endl;
return 1;
}
if (m.getxp() > 100 and m.getxp() < 150) {
unlock[1] = 1;
cout << "Congratulation WaterDragon unlocked " << endl;
return 2;
}
if (m.getxp() > 150 and m.getxp() < 175) {
unlock[2] = 1;
cout << "Congratulation IceDragon unlocked " << endl;
return 3;
}
if (m.getxp() > 175 and m.getxp() < 500) {
unlock[3] = 1;
cout << "Congratulation FireDragon unlocked " << endl;
return 4;
}
if (m.getxp() > 500) {
unlock[4] = 1;
cout << "Congratulation DarkDragon unlocked " << endl;
return 5;
}
}
bool Dragon::if_locked(int x) {
if (unlock[x] == 1) {
return true;
}
else
return false;
}
*Im not comfortable with smartpointer(i have never used them before but i would love to use them if you show me how to call it main *
I used raw pointer but it is still showing me errors please help mw
The idea is sound, it's just the execution that failed.
If you want to have a factory function that will return different objects that will then behave polymorphically, this is perfectly fine, with one important thing - you need to return actual objects.
Your factory function has a return type of Dragon*. This means that whatever value you return, it will be a pointer pointing to (some sort of a) Dragon. However, the value of that pointer can point to an object that is actually an instace of FireDragon, IceDragon etc. When you create an instance of such an object, the pointer to such instance can then get converted to the appropriate return type and returned.
In your case though, while you're creating temporary objects of type pointer-to-some-sort-of-dragon, you're not actually filling them with instances. They all are created with an unspecified value, and that value is then converted to an unspecified value of type Dragon*, with no way to extract the information which type it was converted from.
So, in order to make it work in a usual fashion, we just need to create a new instance of appropriate type and return it. We don't ever want to return raw pointers from functions when transferring ownership, so std::unique_ptr is a much better alternative:
std::unique_ptr<Dragon> Dragon::spawn() {
int x = rand() % 5;
/* note - this bit of code doesn't make any sense whatsoever
int y;
if (!if_locked(x)) //is a different function
spawn();
else
y = unlocking(m); //is also a different function
*/
// note that `rand() % 5` will produce values 0 through 4.
switch(x) {
case 0: return std::make_unique<GroundDragon>();
case 1: return std::make_unique<WaterDragon>();
case 2: return std::make_unique<IceDragon>();
case 3: return std::make_unique<FireDragon>();
case 4: return std::make_unique<DarkDragon>();
}
}
Now you don't need the spawner function at all; you can directly use the returned value.
I think the first important lesson to give is about initializing pointers.
The line
GroundDragon* pt;
declares that there is a pointer to a GroundDragon object, so far so good. However, this does not create an object.
As an analogy: you created a direction sign that is able to point into the direction of a village, but you do not build an actual village for it to point towards. And right now, it just points into some random direction.
Being uninitialized, pt contains a random value, not the address of an existing object of type GroundDragon. Doing anything with it will most likely result in bad things (it is undefined behaviour in particular).
In order to initialize it, you would write it like
GroundDragon* pt = new GroundDragon;
This creates an objects of type GroundDragon on the heap and assigns it's address to pt.
Also note that every new needs a delete to keep the memory clean, just to mention this preemptively.
This is very essential knowledge - make sure to understand this.
I wanted to write this in order to show you how to work with raw pointers, as an addition to the answer of Bartek Banachewicz, who did not show the way to initialize raw pointers. What he then does, using smart pointers, is clearly a way better approach than using raw pointers, though. However, I'd say that it is quite important that you are also able to work with raw pointers. Make sure to be able to do both in the long run, and use smart pointers whenever you can.
Edit: Now for the other part of your code,
int x = rand() % 5;
int y;
if (!if_locked(x)) //is a function to see if that id is unlocked because i want some dragon to be generated only if you have certain xp so it will call func again until unlocked id is generated
spawn();
else
y = unlocking(m);
First of all, please be aware that calling spawn(); ignores the return value. Which means, it has no effect at all. What you probably wanted to write is return spawn();.
Second, if(!if_locked(x)) seems to me to be the contrary of what it should be. "if not locked" means "if unlocked", and in that case, it should not try it again but instead proceed, right?
And, is the Mage m that you give to unlocking a member of Dragon? Sounds like it rather should be a pointer, if you don't especially want a relationship like Dragon owns Mage.
In any case, I would keep unlocking out of spawn. A method like spawn says that it is good for spawning. Especially printing that something is unlocked is not something I would intuitively understand a part of the spawning process. Also, I would name it differently, as "unlocking" is a status rather than a command. Go like check_for_new_unlocks or something like that, which sounds like a command. And do it separately from spawn.
Also, note that you check for narrow experience intervals - are you sure that it can't happen that an interval is never triggered, as the character might advance over the interval with never calling the method in between?
Furthermore, I'd call the array that says if something is unlocked differently. "unlock" sounds like a command. How about "available"?
Also, I find the recursive call of spawn to be less readable as trying out other values for x, but that is opinion. I'd go like
int x = rand() % 5;
while(not available[x])
{
x = rand() % 5;
}
Maybe this can be done more clever, though, based on the actual mechanics. You could create the random variable like int x = rand() % total_available(); for instance.
Note that a lot of this is somewhat opinion based. In this regard, I want to point you towards CodeReview - as soon as your code works correctly, you might want to post it there for people to help you improve it in several different regards.

C++ Class Variable not updating

I'm trying to code a counter for a class I made in c++.
I'm passing an amount of time, deltaT, to a method of the Wake class, which does simply adds it onto the value already stored by a variable in the class. The code is:
void checkPlayerWakes(int deltaT){
for(size_t i = 0; i < game.getPlayer().getWakes().size(); i++){
Wake& w = game.getPlayer().getWakes().at(i);
w.age(deltaT);
}
}
However, the timer which is meant to be increasing, is remaining at 0.
The code to change the timer is:
void Wake::age(int millis) {
cout << "Updating : " << currentLife;
this->currentLife += millis;
setAlpha(((double)currentLife)/((double)lifeTime));
cout << " " << currentLife << endl;
}
E.g.
first current life: 0
second current life: 16
I know that if I were to use
Wake w = something
w.age(deltaT)
it wouldn't work because "w" would just be a copy of the object. However, that's clearly NOT my problem here? Also game.getPlayer() also returns a reference, a PLAYERSHIP&.
Last time I had a similar problem, it was solved by calling and returning references instead of just the ClassName. Am I still doing something wrong?
getWakes() needs also to return a reference, and should return vector& as opposed to vector.
I'm adding this here to make sure that this question is marked as answered.

Building a dynamically allocated array of class Objects

First off, if this problem seems incredibly easy to you, I want to in advance apologize but I am only a beginner.
I have been stuck now for about a week with this problem and it is getting ridiculous since it shouldn't be that hard, even for a complete beginner like me.
I am writing a program which reads a bunch of information regarding receipts from a text file, like name, sum, date etc. and then prints it out to the screen. Simple enough, right? Well I started with using static arrays in my two classes Transaction and TransactionsList and it was working fine, I was printing the contents of the file to the screen just fine one line after the other.
Now I need to do this using dynamic arrays.
Each line in the text file contains a date, type, name, sum, number of friends and name of those friends which should be read an stored as a Transaction class object inside the dynamic array trans. This is what I am having trouble understanding no matter how much theory and googling I do on the subject. Where should I use an overloaded assigment operator, where a copy constructor and how do I call them properly? I have read up on these concepts but I can't use them in my program still. These are questions just flying around in my head right now.
I have changed the arrays friends and trans to be declared as pointers which I understand is correct. I then want to allocate memory for the arrays with "new", but here I am starting to get unsure just where I allocate with new, inside the contructors of their classes or inside the functions where they are needed?
I realize vectors is the answer to alot of these problems but I should tell you that I have not gotten into vectors yet, so I am trying to solve this problem without vectors. I realize this may be be a bit backwards, but I should be able to build my dynamically allocated array of objects and print it out without vectors I think. I have heard they are more practical but for now I have to understand this assignment without the concept of vectors.
I have read up on difference between shallow copies and deep copies as well and I get the theory, but I just can't implement it somehow. (I am probably retarded I know).
This is what I have got so far:
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
#include <iomanip>
using namespace std;
class Transaction
{
private:
string date;
string type;
string name;
double sum;
int nr_friends;
string *friends;
public:
Transaction();
~Transaction();
Transaction &operator = ( const Transaction &t );
string get_name();
int get_no_friends();
double get_sum();
bool readOneTrans( istream &is );
void writeOneTrans( ostream &os );
};
class TransactionsList
{
private:
Transaction *trans;
int no_Trans;
public:
TransactionsList();
~TransactionsList();
void read( istream & is );
void print( ostream & os );
void add( Transaction & t );
};
int main()
{
ifstream inFile("test.txt");
Transaction t;
TransactionsList tl;
// t.readOneTrans(inFile); // reading just one line works fine (when uncommented)
// t.writeOneTrans(cout); // printing works too just fine
//tl.read(inFile); // here I want to read all contents of file
//tl.print(cout); // and here print out them to the screen
return 0;
}
Transaction::Transaction()
{
date = "000000";
type = "transp";
name = "default";
sum = 0.0;
nr_friends = 0;
friends = NULL;
}
Transaction::~Transaction()
{
delete [] friends;
}
Transaction &Transaction::operator = ( const Transaction &t )
{
if ( this != &t )
{
delete[] friends;
date = t.date;
type = t.type;
name = t.name;
sum = t.sum;
nr_friends = t.nr_friends;
friends = new string[nr_friends];
for ( int i = 0; i < nr_friends; i++ )
{
friends[i] = t.friends[i];
}
}
return *this;
}
string Transaction::get_name()
{
return name;
}
double Transaction::get_sum()
{
return sum;
}
int Transaction::get_no_friends()
{
return nr_friends;
}
bool Transaction::readOneTrans( istream &is )
{
is >> date >> type >> name >> sum >> nr_friends;
friends = new string[nr_friends];
for (int i = 0; i < nr_friends; i++)
{
is >> friends[i];
}
return is;
return !is.eof();
}
void Transaction::writeOneTrans( ostream &os )
{
os << left << setw(10) << date <<
setw(10) << type << setw(10) << name
<< setw(10) << sum << setw(10)
<< nr_friends;
for (int i = 0; i < nr_friends; i++)
{
os << left << setw(8) << friends[i];
}
os << endl;
}
TransactionsList::TransactionsList()
{
no_Trans = 1;
trans = new Transaction[no_Trans];
}
TransactionsList::~TransactionsList()
{
delete [] trans;
}
void TransactionsList::read( istream & is )
{
Transaction t;
while ( t.readOneTrans( is ))
{
add( t );
}
}
void TransactionsList::print( ostream & os )
{
Transaction t;
for (int i = 0; i < no_Trans; i++)
{
t = trans[i];
t.writeOneTrans( os );
}
if (os == cout)
{
os << "\nNumber of transactions: " << no_Trans << endl;
}
}
void TransactionsList::add( Transaction & t )
{
// each time I read a line from the file it is passed in as object t here
// here I want to add this object t to the dynamic array trans somehow
// and keep building the array with a new class object every time
// Probably by overloading assignment operator somehow but how?
trans[no_Trans] = t;
no_Trans++;
// i have no idea what to put here to make it work...
}
So as you can see, what I want to do is continually build up the dynamic array trans with different objects of the class Transaction, each instance representing a different line in the text file I am reading from so that I can print out all the lines in the file to the screen in the end.
The output lines should look like this:
011216 food John 300 2 Nathan Julia
To do this now dynamically, I realize I must copy the contents of object t that is passed in in the method "add" and add it to the array trans and somehow without losing the data of the earlier t:s which are representing the previous text lines. This was easy for me to do while the arrays where static ones, as I just assigned the next element in the array trans to be equal to the current object t (inside the add function). This is how my add function looked with static arrays:
void TransactionsList::add( Transaction & t )
{
trans[no_Trans] = t;
no_Trans++;
}
Obviously this doesn't work when you are working with dynamically allocated memory. I read some theory on this and I understand one cannot change the size of the array while it is running so the array actually has to be deleted and then allocated as a larger array and copy over the old contents using a deep copy, which doesn't just copy the memory address for the dynamic array but makes a new array with the olds content.
As you can see, I have read alot of theory but don't really understand it...
Can anyone help? I would be immensely thankful as I have not learned anything in a week and this is really killing me right now. I need to make progress now!
Some hints about the container:
Don't use using namespace std; (why?)
An unsigned integral size in c++ is usually represented as std::size_t from <cstddef>.
Get familiar with rule of three / rule of three/four/five.
A quite useful idiom that is usually applied to such classes is: 'Resource Acquisition Is Initialization (RAII)'.
Bottom line:
When managing resources we usually need to have
a destructor
a copy constructor
a move constructor
a copy assignment operator
a move assignment operator
Resource aquisition should only happen in the constructor.
Functions such as add should not perform seperate resource acquisition but create a temporary of appropriate size and swap/move contents.
The issue of constructing a dynamically-allocated array is completely separate from the issue of constructing the objects themselves.
class TransactionList {
Transaction *trans;
size_t trans_size;
size_t no_Trans;
public:
TransactionList(size_t initial_size)
: trans(new Transaction[initial_size]),
trans_size(initial_size),
no_Trans(0)
{
}
~TransactionList()
{
delete[] trans;
}
// ...
};
That's it. There's nothing different about your existing add() method. It still works exactly the same way, because of the fact that an array is really just a pointer to the first element in the array, which is still the case here.
But you do need to figure out what to do when no_Trans reaches the actual allocated trans_size. That's going to be your homework assignment.
What you probably want to do, though, is to change this to an array of Transaction * objects, and also dynamically allocate each Transaction when it's added to the array. That will require additional work.
(This answer requires no extra knowledge, and needs only a little bit change of your code)
Things get weird in the constructor:
no_Trans = 1;
trans = new Transaction[no_Trans];
People usually leave some space for future elements to add:
max_Trans = 100;
no_Trans = 0;
trans = new Transaction[max_Trans];
And in add()
if (no_Trans >= max_Trans) { // no more space?
// make a new array that is as twice big as the old one
max_Trans = 2 * max_Trans;
Transaction new_trans = new Transaction[max_Trans];
// copy elements to the new array
for (int i = 0; i < no_Trans; i++)
new_trans[i] = trans[i];
// delete the old one and start to use the new one
delete[] trans;
trans = new_trans;
}
trans[no_Trans] = t;
no_Trans++;
Of course max_Trans can also be 1, and make it grow as 1, 2, 3, 4... But that requires new on each add operation, which is inefficient.

Is the returned string from a recursive function going out of scope?

Giving as simple of a background context as I possibly can, which I don't think is necessary for what I'm trying to figure out at the moment, I'm trying to implement a graph representation via adjacency list, in my case being an unordered map that has a string key to a struct value that contains Vertex object pointers (the object that is identified by the key), and a vector of its dependencies. The goal is to output a critical path via a sort of DAG resolution algorithm.
So when I need to output a critical path, I'm trying to use a recursive solution I implemented. Basically it looks for a base case (if a job has no dependencies), return a print out of its id, start time and length. Otherwise, find the longest running (in terms of time length) job in its dependency list and call the function on that until you find a job with no dependencies. There can be more than one critical path, and I don't have to print out all of them.
MY QUESTION: I'm debugging this at the moment, and it has no problem printing out a job's properties when its a base case. If it has to recurse through though, the string always comes back as empty (""). Is the recursive call making my string go out of scope by the time it comes back to the caller? Here is the code structure for it. All of the functions below are public members of the same Graph class.
string recurseDeps(unordered_map<string, Dependencies>& umcopy, string key) {
if (umcopy[key].deps.empty()) {
string depPath = " ";
string idarg, starg, larg, deparg;
idarg = key;
starg = " " + to_string(umcopy[key].jobatKey->getStart());
larg = " " + to_string(umcopy[key].jobatKey->getStart() + umcopy[key].jobatKey->getLength());
umcopy.erase(key);
return depPath + idarg + starg + larg;
}
else {
string lengthiestDep = umcopy[key].deps[0];
for (auto i = begin(umcopy[key].deps); i != end(umcopy[key].deps); i++) {
if (umcopy[*i].jobatKey->getLength() >
umcopy[lengthiestDep].jobatKey->getLength()) {
lengthiestDep = *i;
}
}
recurseDeps(umcopy, lengthiestDep);
}
}
string criticalPath(unordered_map<string, Dependencies>& um, vector<Vertex*> aj) {
unordered_map<string, Dependencies> alCopy = um;
string path = aj[0]->getId();
for (auto i = begin(aj); i != end(aj); i++) {
if (um[(*i)->getId()].jobatKey->getLength() >
um[path].jobatKey->getLength()) {
path = (*i)->getId();
}
}
return recurseDeps(alCopy, path);
}
Later on down in the class members, a function called readStream() calls the functions like so:
cout << time << criticalPath(adjList, activeJobs) << endl;
You're not returning the value when you recurse. You're making the recursive call, but discarding the value and just falling off the end of the function. You need to do:
return recurseDeps(umcopy, lengthiestDep);
First of all, to answer your question, since you return by value the string is copied so no need to worry about variables going out of scope.
Secondly, and a much bigger problem, is that not all paths of your recursive function actually returns a value, which will lead to undefined behavior. If your compiler doesn't already warn you about this, you should enable more warnings.

Value not being increased after addition statement?

I have two functions where I'm adding random numbers to the total value.
The problem is that every time I call the function and print the total, it does not add. If it generates 2 it will say the total is 2. After that if I call it again and it generates 5, it says the total is 5, and doesn't add (it should be 7 if this happened.)
Everything looks fine here...
int human(int humanscore)
{
int diceRoll= rand() % 7;
cout << "player rolled: ";
humanscore+= diceRoll;
cout << diceRoll;
cout << "human score is: " << humanscore;
}
int computer(int compscore)
{
int diceRoll= rand() % 7;
cout << "computer rolled: ";
compscore+= diceRoll;
cout << diceRoll;
cout << "computer score is: " << compscore;
}
You are modifying the value of the internal copy of the argument passed to the functions. If you want the change to be done on the external variable, change your function definitions to take references instead: int& score.
Also note that rand() % 7 will give you a value in the range [0, 6]. A dice has values in the range [1, 6], you should use 1 + rand() % 6 instead.
* Update: *
This can be done with C++ references:
int computer(int& compscore)
{
...
compscore += diceRoll;
...
}
int var = 0;
computer( var );
For this declaration, the function takes the actual variable var as an argument, and changes done to compscore within the function are reflected in the variable var as compscore and var for that particular invocation are just aliases to the same variable.
In C the same effect is achieved with pointers:
int computer(int* compscore)
{
...
*compscore += diceRoll;
...
}
int var = 0;
computer( &var );
This invocation of the function gives it the address of the variable which should be changed. For general use, you can assume that the first implementation using references will automatically generate a solution by the compiler similar to this last snippet.
You're couting the diceRoll here, so it's only ever going to print the current roll as opposed to the total.
Assuming you're correctly printing the dice roll, you also need to write return humanscore; and return compscore; at the end of those functions to pass back to the new result.
You are passing your parameters by value. This means that any changes to them are thrown away when the function is done. If you want your scores to persist you have a few choices. You could pass the parameters by reference, which in C++ could be done by passing a pointer to the score. You could make your function return the new score, and then pass the returned value to the function the next time you call it. You would also have the significantly less desirable solution of using global variables.