2015-02-11 51 views
0

我在实现一个解决方案时遇到了一些问题,这个解决方案在循环中的堆上处理对象创建,并将其存储在一个数组中。如何高效地管理数组(C++)中的堆对象?

我有一个交易类,表示一个股份交易。它包括自定义日期和时间类以及一些用于存储数值的双精度值。

我从一个包含大约100条交易记录的CSV文件中使用我的TransactionIO类读取这些数据。

我的算法如下:

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 

下面是TransactionIO功能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 
} 

,因为我在做我的数据类。那么使用这个函数的正确方法是什么?

我打算这样做:在主

在循环:

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

这是做正确的方式?我的自定义向量的Add方法需要一个const T &引用来添加给定的对象。

此外,将调用我的自定义向量的内部数组上的删除[]删除其中存储的对象?

我觉得我的代码非常低效,我的讲师警告我不要在循环中构造对象。我应该通过引用返回对象。

但在这种情况下,如果我试过这不会无效?

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? 
} 

在上面的行中,一旦这个函数完成,t1将超出范围,对象将被销毁。那么我的参考文献会指出什么呢?

这就是为什么我决定上的指针的解决方案,但我觉得我的计划将有一个巨大的内存泄漏...

任何帮助和解释将高度赞赏...我想知道不只是如何尽可能地回答你的答案。

最后,没有。我不能使用STL容器。我必须使用我自己的矢量类(我已经测试过它,它运行良好)。

+0

参考指的是你传递的变量 - 该变量(即你的参数)分配一个局部变量的副本。 – molbdnilo 2015-02-11 10:46:41

+1

显然你的讲师今天跳过课堂[RVO](http://en.wikipedia.org/wiki/Return_value_optimization)和[move-semantics](http://stackoverflow.com/questions/3106110/what-is-move - 语义学)进行了讨论。而且“我不能使用STL”。 - 你认为'std :: ifstream','std :: string'和'std :: getline'来自哪里,gnomes?在别的任何事情之前,我要处理的最直接的事情是接受智能指针和浪费 - 可以将所有这些原始指针扔掉,或拥抱RAII建模(或* * *)。检查IO操作成功也是可取的; 'ReadTransaction'没有。 – WhozCraig 2015-02-11 10:52:07

+0

你实际上已经提出了多个问题,这将需要一个详细的答案。您没有检查I/O是否成功,如果I/O失败,这会给出各种问题。在ReadTransaction()中动态分配对象并不是必须的,因为调用者无论如何都会将指针解引用。而且你有内存泄漏,因为动态分配的对象从不释放。 – Rob 2015-02-11 10:58:21

回答

0

我会同意你最好是堆栈分配你的对象。

堆分配相对昂贵,在您的情况下不必要,因为您只是在将对象复制到向量中之前将指针用作临时存储。然后你需要删除那些我没有看到你做的指针。

在这种情况下:

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? 
} 

你最好不要让transaction为参考t1,要指定的t1内容到transaction是一个参考的对象。你甚至可以使用transaction = std::move(t1);明确地使用move-assignment操作符(只要你定义了一个或者你的类符合隐式定义的规则)并且不需要拷贝。

但是,您甚至不需要传入引用,如果您只是按值返回,编译器将会删除副本,而不是直接分配给调用站点。这被称为Return Value Optimisation。因此,你可以做到以下几点:

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

还是在C++ 11:

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

'transaction = std :: move(t1)'不会使用移动构造函数;它会使用* move-assignment *,并且只有类支持它。 'transaction'是一个*存在的*对象。 – WhozCraig 2015-02-11 11:04:53

+0

谢谢。我对如何处理我的解决方案和一些新的研究主题(RVO,移动语义)有了更好的理解。并感谢您对RVO的简要解释。我会标记你的答案,因为我发现它是最有帮助的。 – InfinityMatrix 2015-02-11 11:25:54

0

如果长时间运行,分配循环中的对象效率不高。

你可以做的一件事是预先分配你的矢量,但如果你不知道你需要什么大小,它也可能是低效的。 在STL中,你可以使用reserve:http://www.cplusplus.com/reference/vector/vector/reserve/ 但是在你的情况下,你需要在vector类中实现类似的东西。

在上面的行中,一旦此函数完成,t1将从 范围中消失,并且对象将被销毁。那么我所指的是什么, 指向? 未定义的行为,除非事务是某种智能指针。

此外,将调用我的自定义向量的内部阵列上的delete [] 删除存储在其中的对象?

如果我理解的很好,是的。

所以你可以保留向量的大小(如果你有一个很好的估计大小),然后填充已经创建的插槽。 这样你的矢量不需要频繁地扩大尺寸。

+0

有趣。我实际上有一个类似的功能,但它需要一些重新分解因子。感谢您的建议。 – InfinityMatrix 2015-02-11 11:26:37