0

I am having some issues with implementing a solution that deals with object creation on the heap in a loop and stores this in an array.

I have a transaction class that represents a share transaction. It includes custom date and time classes as well as some double values to store numeric values.

I read these using my TransactionIO class from a CSV file that contains around 100 transaction records.

My algorithm is as follows:

While EOF not reached
  Read Transaction data
  Create Transaction object on heap
  Return pointer to newly created Transaction object
  Store 'Pointed-to-Transaction' in custom Vector
EndWhile

Here is the code for the TransactionIO function ReadTransaction:

// Reads in a transaction and returns it
Transaction* TransactionIO::ReadTransaction(ifstream& is)
{
    // Assuming file structure is a CSV file
    // With structure given as:
    // Date/Time, Price, Volume, Value, Condition

    Date date;
    Time time;
    double price, volume, value;
    string condition;
    string dateString, timeString;

    //cout << "\nBegin Read..." << endl;

    getline(is, dateString, ',');       // read date/time string and then parse
    ReadNextDoubleField(price, is);
    ReadNextDoubleField(volume, is);
    ReadNextDoubleField(value, is);
    getline(is, condition);

    // split the date and time fields and parse them
    timeString = dateString.substr(dateString.find_first_of(" ") + 1);
    dateString.erase(dateString.find_first_of(" "));    // remove the time string - only need date string here

    date = ParseDate(dateString);    // Will change later to use pass by reference
    time = ParseTime(timeString);    // Will change later to use pass by reference

    // construct and return transaction that was read
    Transaction* transaction = new Transaction(date, time, price, volume, value, condition);
    return transaction
}

I have not created my main class yet, as I was working on my data classes. So what would be the correct way to use this function?

I am planning on doing this:

In the loop in main:

While(//file IO condition here...)
{
   p_transaction = TransactionIO::ReadTransaction(is);
   myCustomVector.Add(*transaction);
}

Is this the right way to do it? My custom vector's Add method expects a const T& reference to add the given object.

Also, will calling delete[] on my custom vector's internal array delete the objects being stored in it?

I feel my code is very inefficient and my lecturer has warned me to not construct objects in loops. I should return objects by reference.

But in this case If i tried that wouldn't this be invalid?

void TransactionIO::ReadTransaction(Transaction& transaction, ifstream& is)
{
   // Do all the reading and processing as given above....
   Transaction t1(date, time, price, volume, value, condition);
   transaction = t1;    // creating object AND call assignment op - not efficient?
}

In the above line, once this function completes, t1 will go out of scope and the object will destroyed. So what will my reference be pointing to?

Which is why I decided on the pointer solution, but I feel that my program will have a huge memory leak...

Any help and explanation would be highly appreciated... I like to know not just the 'how' but also the 'why' of your answer if possible.

And lastly, no. I cannot use STL containers. I have to use my own vector class (I have tested it out and it works well.)

  • The reference refers to the variable you passed in - that variable (i.e. your parameter) is assigned a copy of the local variable. – molbdnilo Feb 11 '15 at 10:46
  • 1
    Apparently your lecturer skipped class the day [RVO](http://en.wikipedia.org/wiki/Return_value_optimization) and [move-semantics](http://stackoverflow.com/questions/3106110/what-is-move-semantics) were discussed. And "I cannot use STL." - where did you think `std::ifstream` , `std::string`, and `std::getline` come from, gnomes? The most immediate thing I would tackle before anything else is to either embrace smart pointers and waste-can all this raw pointer tossing, or hug an RAII modeling (, or *both*). Checking IO operation success would be advisable as well; `ReadTransaction` has none. – WhozCraig Feb 11 '15 at 10:52
  • You've actually asked multiple questions, which would necessitate a long and detailed answer. You're not checking that I/O succeeds, which will give all sorts of problems if I/O fails. It is not really necessary to dynamically allocate your objects in ReadTransaction(), since the caller dereferences the pointer anyway. And you have a memory leak, since the dynamically allocated objects are never released. – Rob Feb 11 '15 at 10:58
  • Alright. Thank you all for your answers. IO issues was next on my agenda as I have been given the input file and have structured my IO class specifically for this format. – InfinityMatrix Feb 11 '15 at 11:20
  • Also, to the guy getting upset at me for referring to STL and the tirade against my lecturer. I have edited it to "STL containers"... All good now? Thank you though, I will research on RVO and move semantics and discuss this with my lecturer.... – InfinityMatrix Feb 11 '15 at 11:21

2 Answers2

0

To allocate the object in a loop is inefficient if it has a long run.

One thing that you can do is to pre-allocate your vector, but it can be inefficient too if you have no idea what the size you need. In STL you could use reserve: http://www.cplusplus.com/reference/vector/vector/reserve/ But in your case you need to implement something similar in your vector class.

In the above line, once this function completes, t1 will go out of scope and the object will destroyed. So what will my reference be pointing to? Undefined behavior, except if Transaction is some kind of smart pointer.

Also, will calling delete[] on my custom vector's internal array delete the objects being stored in it?

If I understood well, yes.

So you can reserve the size of the vector (if you've a good estimate size), and then populate the already created slots. This way your vector doesn't need to expand the size so frequently.

dfranca
  • 5,156
  • 2
  • 32
  • 60
0

I would agree that you are better off stack-allocating your objects.

Heap allocation is comparatively expensive and in your case unnecessary as you are only using the pointers as temporary storage before copying your objects into the vector. You would then need to delete those pointers, which I don't see you doing.

In the case of this:

void TransactionIO::ReadTransaction(Transaction& transaction, ifstream& is)
{
   // Do all the reading and processing as given above....
   Transaction t1(date, time, price, volume, value, condition);
   transaction = t1;    // creating object AND call assignment op - not efficient?
}

You are not making transaction into a reference to t1, you are assigning the contents of t1 to the object which transaction is a reference to. You could even do transaction = std::move(t1); to explicitly use the move-assignment operator (so long as you have defined one or your class meets the rules for implicit definition) and eliminate the need for a copy.

However, you don't even need to pass in a reference, if you just return by value the compiler will elide the copy, instead allocating directly into the call site. This is known as Return Value Optimisation. As such, you can just do the following:

Transaction TransactionIO::ReadTransaction(ifstream& is)
{
   // Do all the reading and processing as given above....
   return Transaction(date, time, price, volume, value, condition);
}

Or in C++11:

Transaction TransactionIO::ReadTransaction(ifstream& is)
{
   // Do all the reading and processing as given above....
   return {date, time, price, volume, value, condition};
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • 1
    `transaction = std::move(t1)` would not use a move-constructor; it would use *move-assignment*, and that only the class supports it. `transaction` is an *existing* object. – WhozCraig Feb 11 '15 at 11:04
  • Thank you. I have a better understanding of what to do with my solution and some new topics to research on (RVO, move semantics). And thank you for a brief explanation on RVO. I'll mark your answer since I found it to be the most helpful. – InfinityMatrix Feb 11 '15 at 11:25