Dynamic array of objects - c++

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.

Related

Dynamically changing the size of an array and reading in values. (w/o vectors)

Hello I am having the following difficulty,
I am trying to read in a table of doubles (1 entry per line) and store it in an array, while dynamically changing this array's size (for each line/entry). This is for a school assignment and it forbids the use of vectors(would be much easier...). The main idea that I had is to have a main array which stores the value, then store the previous values and the next one into a new array and do this iteratively. Currently, the problem that I am having is that only the last value of the table is being stored. I am aware, that somehow I need to be passing the data by refference to the global function and that the pointers that I am working with become null ater they exit the following iteration of the while. However, since the exact length of the data is unknown, this seems impossible since intializing an array in the main() is impossible (exact length not known). Any help would be appreciated.
Code posted below.
EDIT: after consideration of the two comments I made the following changes to the code, however I am not sure, whether they will behave appropriately. I added a new function called add_new_datapoint, that should globally change the values of the pointer/length and this is done by passing the values by refference. Called in the problematic else statement as add_new_datapoint(data_ptr, data_len, new_dp). Also, I am not sure that reallocating new memory to the pointer variable, will not result in a memory leak. In essence (after I reallocate data_ptr is the memory that was 'being pointed to' released or do I have to delete it and then re-inialise it in the . In such case, can I refference the pointer 'data_ptr' again in the next iteration of the loop?
I think it will be easier to simplify your posted code than trying to find all the places where you could have errors.
If you expect to see only double values in your file, you can simplify the code for reading data from the file to:
while ( data_file >> new_data_pt )
{
// Use new_data_pt
}
If you expect that there might be values other than doubles, then you can use:
while ( getline(data_file, line) )
{
std::istringstream str(line);
while ( str >> new_data_pt )
{
// Use new_data_pt
}
}
but then you have to understand the code will not read any more values from a line after it encounters an error. If your line contains
10.2 K 25.4
the code will read 10.2, encounter an error at K, and will not process 25.4.
The code to process new_data_pt is that it needs to be stored in a dynamically allocated array. I would suggest putting that in a function.
double* add_point(double* data_ptr, int data_len, double new_data_pt)
Call that function as:
data_ptr = add_point(data_ptr, data_len, new_data_pt);
Assuming the first while loop, the contents of main become:
int main()
{
std::fstream data_file{ "millikan2.dat" };
// It is possible that the file has nothing in it.
// In that case, data_len needs to be zero.
int data_len{ 0 };
// There is no need to allocate memory when there is nothing in the file.
// Allocate memory only when data_len is greater than zero.
double* data_ptr = nullptr;
double new_data_pt;
if (!data_file.good()) {
std::cerr << "Cannot open file";
return 1;
}
while ( data_file >> new_data_pt )
{
++data_len;
data_ptr = add_point(data_ptr, data_len, new_data_pt);
}
// No need of this.
// The file will be closed when the function returns.
// data_file.close();
}
add_point can be implemented as:
double* add_point(double* data_ptr, int data_len, double new_data_pt)
{
double* new_data_ptr = new double[data_len];
// This works even when data_ptr is nullptr.
// When data_ptr is null_ptr, (data_len - 1) is zero. Hence,
// the call to std::copy becomes a noop.
std::copy(data_ptr, data_ptr + (data_len - 1); new_data_ptr);
// Deallocate old memory.
if ( data_ptr != nullptr )
{
delete [] data_ptr;
}
new_data_ptr[data_len-1] = new_data_pt;
return new_data_ptr;
}
The code to track the number of bad points is a lot more complex. Unless you are required to do it, I would advise to ignore it.
You already got an excellent answer but I figured it may be helpful to point out a few mistakes in your code, so you can understand why it won't work.
In the second else scope you declare data_ptr again, even though it is visible from the outer scope. (delete[] doesn't delete the pointer itself, it just deallocates the memory the pointer points to.)
else {
double* data_temp { new double[data_len] };
std::copy(data_ptr, data_ptr + data_len - 2, data_temp);
*(data_temp + data_len - 1) = new_data_pt;
delete[] data_ptr;
double* data_ptr{ new double[data_len] }; // <- Right here
//for (int j{1}; j < data_len; j++) *(data_ptr + j) = *(data_temp + j);
std::cout << std::endl;
}
Instead you could just write data_ptr = new double[data_len]. However, that alone won't make this work.
All of your data disappears because on every iteration you create a new array, pointed to by data_temp and copy the data there, and on the next iteration you set data_temp to point to a new array again. This means that on every iteration you lose all data from previous iterations. This also causes a memory leak, since you allocate more memory every time you hit this line:
double* data_temp { new double[data_len] };
but you don't call delete[] data_temp afterwards.
I hope this helps to understand why it doesn't work.

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.

insert into a list of pointers 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.

I am stuck with copying a class given an address, I am segfaulting

I have struggled with it for a while but I really can't get it, I am just getting segfaults. I am trying to copy a class, the function I am writing to copy is also below. Crossed out are combinations that I have tried in vain, it's time to call for help
class Scene
{ private:
int max;
int* x_row, *y_col; // maximum and min coordinates of each image
Image**image_layers;
}
void Scene::_copy(const Scene &source)
{
max = source.max;
x_row = new int[source.x_row];
y_col = new int[source.y_col];
image_layers = new Image*[source.max];
for(int i = 0; i < source.max; i++)
{
if(source.image_layers[i] != NULL)
{
//image_layers[i] = new Image(*(source.image_layers[i]));
// image_layers[i] = new Image;
//*image_layers[i] = *source.image_layers[i];
// image_layers[i] = source.image_layers[i];
}
else
{
image_layers[i] = NULL;
}
x_row[i] = source.x_row[i];
y_col[i] = source.y_col[i];
}
EDIT:
I forgot to say that this function is called as " scene(*set) "
The segfault happens here or because of this:
x_row = new int[source.x_row];
y_col = new int[source.y_col];
On the right hand side, you use the address source.x_row as an array size. This is a very large number that most likely will cause the allocation to fail.
You need to keep a member for holding the size or better yet use a std::vector<int> object instead.
Copying C arrays are done faster with memcpy. With C++ vectors, you can just assign one to the other:
x_row = source.x_row
Nothing to do with the question, but this function should be named operator=, will make using the class easier by assigning one instance to another:
Scene & Scene::operator=(const Scene &source)
{
// copy elements
...
return *this;
}
x_row = new int[source.x_row];
y_col = new int[source.y_col];
Are you sure about above code?
source.x_row is pointer

error while extending an array: no operator found which takes

I want to extend my array by one when i call the addBranch function, and then add the new Branch object to the that extended empty area, and I am trying to prevent my code from memory leak. I thought i was doing right but i am stuck with this function.
it gives me error "binary '=' : no operator found which takes a right-hand operand of type 'Branch *' (or there is no acceptable conversion)". I need some help here assigning the last element to a new Branch object that won't be deleted after extermination of function. I am just new to C++, so there might be some big mistakes. I am not allowed to use vectors etc.
// ------- Adds a branch to system -------- //
void BankingSystem::addBranch(const int id, const string name){
if(isBranchExisting(id)){
cout << "\n\tBranch " << id << " already exists. Please try it with another id number.";
}
else if(!isBranchExisting(id)){
Branch* tempArray = new Branch[cntAccounts];
for(int i = 0; i<cntAccounts; i++){
tempArray[i] = allBranches[i];
}
delete[] allBranches;
allBranches = new Branch[cntAccounts+1];
for(int i = 0; i<cntAccounts; i++){
allBranches[i] = tempArray[i];
}
allBranches[cntAccounts] = new Branch(id, name); // error at this line
}
}
As the error message says, you're trying to assign a pointer to an object. You (probably) want to assign an object instead:
allBranches[cntAccounts] = Branch(id, name); // no "new"
I would also suggest you use std::vector<Branch> rather than hand-forged arrays. This will fix the memory leak from forgetting to delete tempArray, or from something throwing an exception if you do add the missing delete[].
Also, if you do use vector, then the whole dance can be replaced with
allBranches.push_back(Branch(id, name));