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));
Related
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.
I am working on a lab dealing with queues, which I don't think is entirely relevant. My task is to create a "priority queue" and the best way I could think of to do it is as follows
void IntQueue::enqueue(int num,int priorityOfEntry)
{
if (isFull())
cout << "The queue is full.\n";
else
{
// Calculate the new rear position
//insert correct lab code here haha
if (priorityOfEntry == 1)
{
rear = (rear + 1) % queueSize;
queueArray[rear] = num;
queueSize++;
}
else if (priorityOfEntry == 2)
{
queueSize++;
int* newArray = new int[queueSize];
newArray[0] = num;
for(int counter = 0;counter< queueSize; counter++)
{
newArray[counter+1] = queueArray[counter];
}
queueArray = newArray;
delete [] newArray;
}
else cout << "invalid priority" << endl;
// Insert new item
// Update item count
numItems++;
}
}
I only have 2 priority levels, 1 and 2, that I explain in the main program. when they all have equal priority it of course works fine, but when I bump on up in priority it throws an error at my destructor.
I really don't think this is the right way to approach this lab, but It seems to work.. at least if I can actually get this memory error fixed.
I figure the only problem could be in that I change the address of what the destructor thinks it will delete.. but I thought pointers would already kind of account for that.
I understand I need to learn to debug my own programs. I really do. but sometimes I just stare at code and there is nothing but a brick wall there. Guess that's what a nudge in the right direction is for.
queueArray is a dangling pointer after this:
queueArray = newArray; // Both 'queueArray' and 'newArray' point to
// the same memory after this assignment.
delete [] newArray;
as the memory that queueArray is pointing to has been deleted. Any attempt to access or destroy queueArray is accessing memory that has already been destroyed. The correct order is:
delete[] queueArray;
queueArray = newArray;
Additionally, there is a potential out-of-bounds access in the for loop that performs the copying:
for(int counter = 0;counter< queueSize; counter++)
{
// When 'counter == queueSize - 1'
// 'newArray[counter + 1]' is one past the end.
newArray[counter+1] = queueArray[counter];
}
Here:
queueArray = newArray; // queueArray and newArray point to the same place
delete [] newArray; // that place gets delete[]ed
you are making queueArray point to the same place as newArray, but then you are deleting the array that lies in that location. So queueArray is left pointing to memory you have given back to the OS, i.e it is now a dangling pointer.
You need to delete queueArray[] first, then assign newArray to it.
Okay, I got it, I don't know why I thought I needed to add another member of the array when the priority switched, I think i'm just tired.
So that was the extra array member
and i think that was the only other problem
EDIT: Pastebin links to the entirety of the code at the bottom
for my CS215 course, I was given a class called String215 which is a basic string class to help in the understanding of dynamic memory allocation and pointer arithmetic with char arrays.
The class was given to me in a very basic skeleton form with prototypes but no implementations, along with a test function to test my implementations. I CAN NOT use any C String functions in this assignment.
The part of the program which is troubling is the append function, which just appends a parameter string215 object to the end of the current string215 object.
// Add a suffix to the end of this string. Allocates and frees memory.
void string215::append(const string215 &suffix)
{
char *output = new char[str_len(data)+suffix.length()+1];
for(int x = 0; x < str_len(data); x++) {
*output = *data;
output++;
data++;
}
for(int x = 0; x < suffix.length(); x++) {
*output = suffix.getchar(x);
output++;
}
*output = '\0';
output -= (str_len(data)+suffix.length()+1);
delete[] data;
data = output;
}
This portion of the code is tested in the 13th test of the test function as shown here:
string215 str("testing");
...
// Test 13: test that append works in a simple case.
curr_test++;
string215 suffix("123");
str.append(suffix);
if (strcmp(str.c_str(), "testing123") != 0) {
cerr << "Test " << curr_test << " failed." << endl;
failed++;
}
Here is the description of the append class:
Add the suffix to the end of this string. Allocates a new, larger, array; copies the old contents, followed by the suffix, to the new array; then frees the old array and updates the pointer to the new one.
My program aborts at the very end of the append function execution with the error message:
Debug Assertion Failed!
Program: [Source path]\dbgdel.cpp
Line: 52
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
...
Abort || Retry || Ignore
I'm fairly certain it has something to do with my very poor memory management. I know it's not a lot to go on, but I've been struggling with this for hours on end and can't seem to figure it out.
Here's a pastebin of the .cpp and .h file for this program
string215.cpp: http://pastebin.com/Xh2SvDKJ
string215.h: http://pastebin.com/JfAJDEVN
Any help at all is greatly appreciated!
Thanks,
RAW-BERRY
You are changing data pointer before delete[]. You need to delete[] exactly the same value you got from new[].
Also, you are incrementing output pointer str_len(data)+suffix.length() times, and you take it back by str_len(data) + suffix.length() + 1.
I would use separate variables for iteration to solve these problems.
You increment output exactly str_len(data) + suffix.length() times. Note that you don't increment output after *output = '\0';.
So to go back to the start, you should use:
output -= (str_len(data) + suffix.length());
By the way, some of the code is not very efficient. For example, getchar uses a loop instead of simply returning data[index]. You use getchar in append, which means that the performance isn't great.
EDIT: As zch says, you use delete[] data after modifying data, but note that even before that you use str_len(data) after modifying data (when deciding how many bytes to go skip back), so the calculation is wrong (and my suggestion above is also wrong, because str_len(data) is now zero).
So I think your problem is with the line
for(int x = 0; x < str_len(data); x++) {
Notice that the size of 'data' is changing at each iteration of the loop. As you increment 'x', you are decreasing the length of 'data'. Suppose 'data' is a string holding "hello": in the first iteration of the loop x=0 and str_len(data)=5; in the second iteration x=1 and str_len(data)=4. Thus the for loop executes half as many times as you need it to and 'data' does not end up pointing to the end of the data string
I'm relatively new to C++ memory management, and I'm getting this weird error of heap corruption (plus an automatic breakpoint in Visual Studio before it). Here is the offending code:
z_world::z_world(char* name)
{
unsigned int i, skip;
char tmp;
//Load data from file
std::string* data = loadString(name);
//Base case if there is no world data
tiles = NULL;
w = 0;
h = 0;
if(data->length() > 0) {
//Set up the 'tiles' array
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n')
h++;
if(h == 0)
w++;
}
tiles = new int[data->length()-h];
//Load Data
skip = 0;
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n') {
skip++;
printf("\n");
continue;
}
tmp = data->at(i);
tiles[i+skip] = atoi(&tmp);
printf("%i ",tiles[i+skip]);
}
}
delete data;
}
Here's where I load in the string:
std::string* loadString(char* name)
{
ifstream in(name);
std::string* input = new string();
while(in) {
std::string line;
getline(in,line);
input->append(line);
input->append("\n");
}
in.close();
return input;
}
I get the breakpoint and error inside of "delete data;", which makes me think that "data" gets deleted somewhere before that, but I can't find where it would. For reference, this method is to create an object that contains world data for a game in the form of a virtual 2D integer array (for the ID's of the tiles).
Youre problem is probably here:
tiles[i+skip] = atoi(&tmp);
Problem 1:
It should be -skip
tiles[i - skip] =
Problem 2:
The atoi() command is being used incorrectly (tmp does not contain a string). But also I don't think atoi() is the appropriate method. I think what you are looking for is simple assignment. The conversion from char to int is automatic:
tiles[i - skip] = tmp;
Problem 3:
You are not using objects correctly. In this situation there is no need to generate dynamic objects and create a mess with dynamic memory management. It would be simpler to just to create automatic objects and pass those back normally:
std::string* loadString(char* name)
// ^ Don't do this.
std::string loadString(std::string const& name)
// ^^^^^^^ return a string by value.
// The compiler will handle memory management very well.
In general you should not be passing pointers around. In the few situations where you do need pointers they should be held within a smart pointer object or containers (for multiple objects) so that their lifespan is correctly controlled.
atoi(&tmp);
atoi expects a pointer to a null terminated string - not a pointer to a char
There's no need to dynamically allocate the string in the code you've shown. Change the loadString function to
std::string loadString(char* name)
{
ifstream in(name);
std::string input;
// ...
return input;
}
In the caller
std::string data = loadString( name );
Now there's no need to delete the string after you're done.
Instead of
int *tiles = NULL;
tiles = new int[data->length()-h];
use
std::vector<int> tiles;
tiles.resize(data.length() - h);
Also, if you do need to dynamically allocate objects you should be using smart pointers (std::unique_ptr and std::shared_ptr) instead of raw pointers.
There is a bug in
tiles[i+skip] = atoi(&tmp);
For example, for a string
Hello\n
World\n
and for the loop iteration at the point of i == 10, skip is already 1 (since we have encountered the first \n before) and you are writing to tiles[10 + 1], but tiles only has been allocated as an array with 10 elements.
May be the variable input is local to this function. So after returning from this the memory is freed. So, calling later delete on this string tries to free already freed memory.
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.