insert into a list of pointers c++ - c++

I'm trying to go over a list of Movie instances, and add one of them to a list of Movie* which is inside Worker class. problem is, when i try to print out this movie from the WorkerClass, mean to go over the Movie* list of Worker class, it prints garbage.
void MovieIndustry::addWorkerToMovie(string code, int id) {
Worker* toBeAdded;
Movie movieToAdd;
for (std::list<Worker*>::iterator it=workers.begin(); it !=
workers.end(); ++it)//look for Worker
{
toBeAdded = *it;
for (std::list<Movie>::iterator it2=movies.begin(); it2 !=
movies.end(); ++it2) //look for movie
{
if( it2->getCode() == code)
{
movieToAdd = (*it2);
it2->addWorker(*toBeAdded); //Works
(*it)->addMovieToWorker(movieToAdd);
}
}//end for
} //end for
}
Trying to print a movie from the list in Worker.cpp, it gives me:
void Worker::printWorkersMovies() const {
std::list<Movie*>::const_iterator movitt;
if(getNumOfMovies() >> 0)
{
for (movitt=moviesOfWorker.begin(); movitt != moviesOfWorker.end();
++movitt)
{
std::cout << "length is" << ( (*movitt)-> getLength() ) <<std::endl;
// IT PRINTS OUT length is: 943829758935781957
// even though I inserted length = 2
}
}
}
Copy constructor of Movie, even though i wrok with pointer:
Movie::Movie(const Movie& toCopy) {
code = toCopy.getCode();
name = toCopy.getName();
length = toCopy.getLength();
year = toCopy.getYear();
rate = toCopy.getRate();
preview = toCopy.getPreview();
}
My method of adding a movie to Worker instance:
void Worker::addMovieToWorker(Movie& toAdd){
moviesOfWorker.push_back(&toAdd);
}

You're adding pointers to automatic storage to your list. As soon as addWorkerToMovie returns, movieToAdd is destroyed, but you still have a pointer to the memory that it used to be in. The behavior when you later use that pointer is undefined, but in your case your program has re-used that memory for something else, and it just so happens to have stored 943829758935781957 where your movie's length used to be.
To make this work, you need to store pointers to objects that will still exist when you need to use them later. It looks like your Worker class has a list of Movies, so you may be able to store a pointer to one of those, instead of making a copy. If not, you may need to dynamically allocate your objects using new, in which case you'll need to make sure to delete them when you're done with them.

Related

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.

Vector iterators incompatible... but why?

I receive the message "Vector iterators incompatible". I tried to wrap my head around it, but nothing. I did it before. Same code, just not used in a class that receives "cWORLD* World". What am I doing wrong?
Thank you!
else if (Click[2] == true)
{
//go through objects and check collision
for (vector<cOBJECT*>::iterator it = World->ReturnWorldObjects().begin(); it != World->ReturnWorldObjects().end();)
{
//Check for collision and delete object
if (PointInRect(MouseX + offX, MouseY + offY, (*it)->getrect()) == true)
{
// delete object, delete slot, pick up next slot
delete *it;
it = World->ReturnWorldObjects().erase(it);
}
else
{ // no action, move to next
++it;
}
}//for
}//else if (Click[2] == true)
Looks like ReturnWorldObjects returns copy of vector, not reference. In this case, you are trying to compare iterators of different objects, that is not checked by standard, but can be checked by checked iterators (in this case, I think it's MSVC checked iterators).
Like #ForEveR already mentioned, you possibly return a copy of a vector in the function ReturnWorldObjects(). Without seeing the declaration of this method I can only assume it's something like vector<cOBJECT*> ReturnWorldObject();
You can come around this with 2 Solutions, I think:
1. Return a reference to the vector in your World Class
const vector<cOBJECT*>& ReturnWorldObjects()
{
return m_vecWorldObjects; // Your vector here
}
2. Get one copy of that function and use that in your code
...
vector<cOBJECT*> worldObjects = World->ReturnWorldObjects();
for (vector<cOBJECT*>::iterator it = worldObjects.begin(); it != worldObjects.end(); it++)
{
...
}
...

Segmentation fault trying to dereference a pointer from a vector of pointers

I have a vector of pointers to objects that I am iterating through using std::vector::iterator`. Since the element returned is itself a pointer I dereference the iterator twice, once to return the pointer and once to resolve the pointer to the actual object.
I'm trying to invoke a member function (getClass) that returns an std::string and I have tried both (**it).getClass() and (*it)->getClass() but both give me a segmentation fault. I keep feeling like I'm missing something obvious.
partial function code:
void dataSet::createFolds()
{
// Shuffle the data vector
std::random_shuffle( m_records.begin(), m_records.end());
std::cout << "STARTING MAIN LOOP. THERE ARE " << m_records.size() << " RECORDS\n";
// iterate through the data vector and assign each to a fold
std::vector<dataRecord *>::iterator it = m_records.begin();
while (it != m_records.end())
{
std::string currentClass = (*it)->getClass(); // SEG FAULT HERE
.
.
.
}
.
.
.
}
The vector is m_records ... code
dataRecord is defined like this ... code
In response to questions about filling the vector:
The data is read from a text file and I really don't want to post the entire thing unless I have to (212 lines) but the pertinent code for populating the vector is below. The constructor for the dataRecord object takes a vector of field objects. I use a temporary pointer, use new to create the object then push_back the pointer.
while ...
{
std::vector<field> fields;
// build the fields vector
for (unsigned int i = 0; i < numAttribs; ++i)
fields.push_back(field(data.at(i), attribTypes[i]));
// create the new dataRecord
dataRecord * newRecord = new dataRecord(fields);
// add the record to the set
m_records.push_back(newRecord);
++recordNum;
std::cout << "read record " << recordNum << std::endl;
}
In my opinion the vector elements are badly initialized. Perhaps you have to test the code that fill the vector independently before testing to extract them. Sorry for my english ;)
Either the pointers in your containers are null, or they are dangling pointers to free'd memory.
Double check the code that fills m_records.
In
std::string dataRecord::getClass() {return m_data.at(m_data.size() - 1).getTextData();}
You must to verify m_data.size() because could be 0, so you will get an exception of out or range.
// create the new dataRecord
dataRecord * newRecord = new dataRecord(fields);
I'm guessing the bug is in dataRecord's constructor. Are you sure it's doing it's job properly?
This doesn't necessarily apply to the OP's question but for those arriving here from googling...
If you're receiving segfaults on dereferencing vector iterators and are working in multi-threaded applications, remember that vectors aren't necessary thread-safe!
The following example is unsafe without mutex locks.
Thread 1
myVector.push_back(new Object());
Thread 2
std::vector<Object*>::iterator = myVector.begin();
for (it; it != myVector.end(); it++) {
Object* obj = *it;
}
Instead, something like this should be done:
myMutex.lock();
myVector.push_back(new Object());
myMutex.unlock();
Thread 2
myMutex.lock();
std::vector<Object*>::iterator = myVector.begin();
for (it; it != myVector.end(); it++) {
Object* obj = *it;
}
myMutex.unlock();

Invalid heap error when trying to copy elements from a Map to a compatible priority Queue

My program makes a frequency map of characters (which I store in , surprise surprise, a Map), I am trying to copy each element from this map into a Priority Queue so that I can have a sorted copy of these values (I plan to make further use of the Q, that's why am not sorting the map) , but whenever I try to copy these values , the program executes fine for the first two or three iterations and fails on the fourth citing an "Invalid heap" error.
I'm not sure how to proceed from here, so I am posting the code for the classes in question.
#include "srcFile.h"
#include <string>
#include <iostream>
srcFile::srcFile(std::string s_flName)
{
// Storing the file name
s_fileName= s_flName;
}
srcFile::srcFile()
{
// Default constructor (never to be used)
}
srcFile::~srcFile(void)
{
}
void srcFile::dispOverallMap ()
{
std::map<char,int>::iterator dispIterator;
dispIterator = map_charFreqDistribution.begin();
charElement *currentChar;
std::cout<<"\n Frequency distribution map \n";
while(dispIterator != map_charFreqDistribution.end())
{
std::cout<< "Character : " << (int)dispIterator->first << " Frequency : "<< dispIterator->second<<'\n';
currentChar = new charElement(dispIterator->first,dispIterator->second);
Q_freqDistribution.push(*currentChar);
dispIterator++;
// delete currentChar;
}
while(!Q_freqDistribution.empty())
{
std::cout<<'\n'<<"Queue Element : " << (int)Q_freqDistribution.top().ch_elementChar << " Frequency : " << Q_freqDistribution.top().i_frequency;
Q_freqDistribution.pop();
}
}
map_charFreqDistribution has already been populated, if I remove the line
Q_freqDistribution.push(*currentChar);
Then I can verify that the Map is indeed there.
Also , both the Q and the use charElement as the template type , its nothing except the character and its frequency, along with 2 pointers to facilitate tree generation (unused upto this point)
Adding the definition of charElement on request
#pragma once
class charElement
{
public:
// Holds the character for the element in question
char ch_elementChar;
// Holds the number of times the character appeared in the file
int i_frequency;
// Left pointer for tree
charElement* ptr_left;
// Right pointer for tree
charElement* ptr_right;
charElement(char,int);
charElement(void);
~charElement(void);
void operator=(charElement&);
};
class compareCharElt
{
public:
bool operator()(charElement &operand1,charElement &operand2)
{
// If the frequency of op1 < op2 then return true
if(operand1.i_frequency < operand2.i_frequency) return true;
// If the frequency of op1 > op2 then return false
if(operand1.i_frequency > operand2.i_frequency)return false;
// If the frequency of op1 == op2 then return true (that is priority is indicated to be less even though frequencies are equal)
if(operand1.i_frequency == operand2.i_frequency)return false;
}
};
Definition of Map and Queue
// The map which holds the frequency distribution of all characters in the file
std::map<char,int> map_charFreqDistribution;
void dispOverallMap();
// Create Q which holds character elements
std::priority_queue<charElement,std::vector<charElement>,compareCharElt> Q_freqDistribution;
P.S.This may be a noob question, but Is there an easier way to post blocks of code , putting 4 spaces in front of huge code chunks doesn't seem all that efficient to me! Are pastebin links acceptable here ?
Your vector is reallocating and invalidating your pointers. You need to use a different data structure, or an index into the vector, instead of a raw pointer. When you insert elements into a vector, then pointers to the contents become invalid.
while(dispIterator != map_charFreqDistribution.end())
{
std::cout<< "Character : " << (int)dispIterator->first << " Frequency : "<< dispIterator->second<<'\n';
currentChar = new charElement(dispIterator->first,dispIterator->second);
Q_freqDistribution.push(*currentChar);
dispIterator++;
delete currentChar;
}
Completely throws people off because it's very traditional for people to have huge problems when using new and delete directly, but there's actually no need for it whatsoever in this code, and everything is actually done by value.
You have two choices. Pick a structure (e.g. std::list) that does not invalidate pointers, or, allocate all charElements on the heap directly and use something like shared_ptr that cleans up for you.
currentChar = new charElement(dispIterator->first,dispIterator->second);
Q_freqDistribution.push(*currentChar);
dispIterator++;
delete currentChar;
In the above code, you create a new charElement object, then push it, and then delete it. When you call delete, that object no longer exists -- not even in the queue. That's probably not what you want.

Dynamic array of objects

I'm aware that I could use something called std::vector, but I'm afraid it's not possible because of the course restrictions.
I need to make a dynamic extensible array of objects. The array should grow and grow when new objects need to be stored.
Here is the class that the array belongs to:
class TransactionList
{
private:
Transaction *trans;
int amountTransactions;
Transaction newTrans;
public:
TransactionList();
~TransactionList();
void read( istream &is );
void write( ostream &os );
void add( Transaction & newTrans );
double totalExpenses();
double hasPaid( string namnet );
double isDebted( string namnet );
//PersonList FixPersons();
};
The method "void add ( Transaction & newTrans )" is the one I need. And yes, I seriously have to do it pointer-style.
So far this method is totally incomplete and just not even close to functional. I've tried several different ways, but end up with a runtime error or just bollocks result.
void TransactionList::add(Transaction & newTrans)
{
Transaction* tempArrPtr;
amountTransactions++;
trans = new Transaction[amountTransactions]
trans[amountTransactions - 1] = newTrans;
}
What I want the method to do is to build an array of of Transaction-objects and grow in size while it gets more objects.
I hope I've written about my problem clearly and wish someone could give me a good answer.
I tried Googling, but I'm still stuck - otherwise I wouldn't have bothered asking :p
Also if someone could give some pointers about copy constructors, I'd be very thankful. In our course material they pretty much didn't even show what a copy constructor should look like. Just like "You need copy constuctors for deep copying, good luck!"
You should add a maxTransactions variable, which would indicate the allocated length of your trans* array, and initialize both ammountTransactions and maxTransactions with 0.
Your array would automatically double its size when we reach the limits of trans
void TransactionList::add(Transaction & newTrans)
{
if(amountTransactions == maxTransactions){ //we've reached the capacity of trans
//allocate a new array
Transaction* nuTrans = new Transaction[maxTransactions*2+1];
//copy the old values of trans into nuTrans
memcpy(nuTrans, trans, sizeof(Transaction)*maxTransactions);
//deallocate the old trans array
delete []trans;
//set trans to point at your newly allocated array
trans = nuTrans;
//update maxTransactions
maxTransactions = maxTransactions*2+1;
}
trans[amountTransactions] = newTrans;
amountTransactions++;
}
PS. I wrote it directly here, I didn't check it if it compiles as a whole or didn't debug the code. But I present it as an idea you could follow
Edit: Working example # http://ideone.com/uz1mE
When you add an object and the array is too small you need to create a new one with the correct or larger size, copy the data, delete the old one and replace it with your new one.
Copy constructors are just like ordinary constructors only that they take an object of the same type. Do remember take care of your pointers properly when doing this.
TransactionList(const TransactionList & o);
Now I finally managed to solve this puzzle. Turns out I wasn't paying enough attention to the fact that my Transaction-objects themselves held a dynamic array, so I finally came up with the idea of making an assign function to copy the objects. Thought I'd share my solution just in case any one has to tackle the same problem with the same limited set of tools.
This is how it ended up looking like:
void TransactionList::add(Transaction & newTrans)
{
amountTransactions++;
cout << "Adding a transaction-object to the array. amountTransactions = " << amountTransactions << endl;
//Allocate a new array
Transaction* tempTrans = new Transaction[amountTransactions];
//Copy the objects with the assign-function
for (int i = 0; i < amountTransactions - 1; i++)
tempTrans[i].assign(trans[i]);
//Delete the old one
delete[] trans;
//Set trans to point at the new one
trans = tempTrans;
//Add the newcomer object
trans[amountTransactions - 1].assign(newTrans);
}
And the assign-function looks as follows:
void Transaction::assign(const Transaction & t)
{
date = t.date;
type = t.type;
name = t.name;
amount = t.amount;
amountFriends = t.amountFriends;
cout << "Hello assign " << amountFriends << endl;
delete [] friends;
if (amountFriends > 0)
{
friends = new string[amountFriends];
for (int i = 0; i < amountFriends; i++)
friends[i] = t.friends[i];
}
else
friends = NULL;
}
I based my final solution on matyas' answer, so I owe you one buddy! :)
Thanks also to Alexandre C. for good read!
I'm not counting out the possibility there might be some error in the code, but at least it compiles, runs and produces correct result. Feel free to point out if you find something that's not right.