C++ 重用对象与创建新对象

C++ 重用对象与创建新对象,c++,C++,我们的一个项目处理大量数据。它从数据库中选择数据,并将结果序列化为JSON/XML 有时,所选行的数量很容易达到5000万大关 然而,程序的运行时间在一开始就很糟糕 因此,我们对程序进行了一次重大调整: 序列化的工作对象不会为每一行重新创建,而是将清除并重新初始化该对象 例如: 之前: 对于每个数据库行,我们创建DatabaseRowSerializer的一个对象,并调用特定的serialize函数 // Loop with all dbRows { DatabaseRowSeriali

我们的一个项目处理大量数据。它从数据库中选择数据,并将结果序列化为JSON/XML

有时,所选行的数量很容易达到5000万大关

然而,程序的运行时间在一开始就很糟糕

因此,我们对程序进行了一次重大调整:

序列化的工作对象不会为每一行重新创建,而是将清除并重新初始化该对象

例如:

之前:

对于每个数据库行,我们创建DatabaseRowSerializer的一个对象,并调用特定的serialize函数

// Loop with all dbRows
{
    DatabaseRowSerializer serializer(dbRow);
    result.add(serializer.toXml());
}
之后:

DatabaseRowSerializer的构造函数未设置dbRow。相反,这将由initDbRow()函数完成

// Loop with all dbRows
{
    DatabaseRowSerializer serializer(dbRow);
    result.add(serializer.toXml());
}
这里的主要问题是,整个运行时只使用一个对象。在对dbRow进行序列化之后,clear()函数 将调用以重置对象

DatabaseRowSerializer serializer;

// Loop with all dbRows
{
    serializier.initDbRow(dbRow);
    result.add(serializer.toXml());
    serializier.clear();
}
因此,我的问题是:

这真的是处理问题的好方法吗? 在我看来,init()函数并不是很聪明。通常,应该使用构造函数初始化可能的参数


你通常喜欢哪种方式?一方面,这是主观的。另一方面,舆论普遍认为C++中应该避免这种“init函数”的习惯用法:
  • 这是更糟糕的代码

    • 你必须记住“初始化”你的对象,如果你没有,它处于什么状态?您的对象永远不应该处于“死”状态。(不要让我开始从“对象”……)这就是为什么C++引入构造函数和析构函数,因为旧的C方法有点混乱,结果程序更难证明正确。
  • 这是不必要的

    • 每次创建
      DatabaseRowSerializer
      基本上没有开销,除非它的构造函数所做的工作超过了
      initDbRow
      函数,在这种情况下,您的两个示例并不等价

      即使您的编译器没有优化掉不必要的“分配”,也不会有真正的分配,因为对象只会占用堆栈上的空间,而且无论如何它都必须这样做

      所以,如果这一改变真的解决了您的性能问题,那么可能还有其他问题

  • 使用构造函数和析构函数。自由而自豪

    <>这是编写C++时的常见建议。
    如果您确实希望序列化程序因任何原因可重用,第三种可能的方法是将其所有状态移动到实际的操作函数调用中:

    DatabaseRowSerializer serializer;
    
    // loop with all dbRows
    {
        result.add(serializer.toXml(dbRow));
    }
    
    如果serialiser希望缓存信息或重用动态分配的缓冲区以提高性能,则可以这样做。这当然会给序列化程序添加一些状态

    如果您这样做了,但仍然没有任何状态,那么整个过程可能只是一个静态调用:

    // loop with all dbRows
    {
        result.add(DatabaseRowSerializer::toXml(dbRow));
    }
    
    …但它也可能只是一个函数

    // Loop with all dbRows
    {
        DatabaseRowSerializer serializer(dbRow);
        result.add(serializer.toXml());
    }
    

    最终,我们无法确切知道什么对您最有利,但有很多选择和考虑。

    大体上,我同意LRiO在报告中提出的观点

    仅仅将构造函数移出循环不是一个好主意

    但是对于这种样式的环体:

  • 向对象提供一些数据
  • 在对象内转换数据
  • 从对象返回转换后的数据
  • IMHO,通常情况下,转换对象会分配一些缓冲区(在堆上),这些缓冲区在使用第二个表单和init函数时可能会被重用。在幼稚的实现中,这种重用甚至可能不是故意的,只是实现的副作用

    因此,如果您看到重构(将对象构造函数从循环中提升出来)加快了速度,那么这可能是因为对象现在能够重用一些缓冲区并避免重复为这些缓冲区分配“冗余”堆

    因此,总而言之:


    您不希望构造函数因为自身的原因而被吊出循环。但是您希望在循环迭代中保留所有可以保留的缓冲区。

    此“主要”更改是否解决了性能问题?否则,一般来说,C++的方式是RAII,即“不<代码>”(< /代码> /<代码>())/代码>,除非你解决一个比维护好的架构更重要的问题。我投票以基于意见的方式结束这个问题。@DevSolar关于这个问题,我们可以说很多客观的东西。这并不完全是“你最喜欢的编译器是什么?”用户2622344应该进行测试并从测试中得到答案。其他人可能会从他们的观点或原则中有所不同。在极少数情况下,可能值得尝试使用一个对象根据某些输入重复作业。您可以将类设计为从输入getWantedValue()构造对象。或者可以构造空对象getWantedValue(inputvalue)。在这种情况下,序列化程序可以是静态的,因为它没有任何状态。正确吗?@user2622344正确。因此,实际上,
    DatabaseRowSerializer::toXml(dbRow)
    可能是第四种有效的方法。什么对你最好取决于我们从这里看不到、知道或测量不到的东西。这是一个很好的观点。从“重复使用”设计开始,允许缓存和缓冲区重复使用以及其他优化,否则您将无法在以后添加这些优化。好吧,除非你再次重构:)