我应该返回堆对象的引用还是返回值?

时间:2017-12-10 09:48:16

标签: c++ performance memory-management reference

我有这两个简单的功能。我认为#define MBGL_DEFINE_ENUM(T, values...)是一个很好的解决方案,因为你通过引用传递了一个对象。我的教科书给出了func1作为最佳解决方案的答案。这只是因为你没有解除分配func2吗?如果我在main中声明heapstr然后将其传递给函数,那么我之后能够删除它会怎么样?

heapstr

3 个答案:

答案 0 :(得分:6)

  

我应该返回对堆对象的引用还是返回值?

按价值返回。

有很多原因,但没有一个与性能有关,因为编译器在优化方面已经足够好了,即使它不是,大多数程序都是I / O绑定的,即等待来自文件或网络套接字的数据的时间会耗尽所有性能,而不是CPU操作本身花费的时间。

例如参见" C ++核心指南"作者:Herb Sutter和Bjarne Stroustrup,他们在"Return containers by value (relying on move or copy elision for efficiency)"部分说:

  

原因

     

简化代码并消除显式内存管理的需要。

至于你的两个职能......

  

我的教科书给出func2作为最佳解决方案的答案。这只是因为你没有解除分配heapstr吗?

内存泄漏是其中一个问题。但重点是,按值返回更简单,更不容易出错。这完全是关于正确性,而不是速度。如果您只能返回int*,则不会返回int,是吗?

  

如果我在heapstr中声明了main然后将其传递给该函数,那么我之后能够删除它会怎么样?

您会在代码中引入很多内存泄漏,崩溃和未定义行为的可能性。它会变得更长,更难写,更难阅读,更难维护,更难调试,更难以在代码审查中证明其合理性。作为回报,你绝对不会获得任何收获。

答案 1 :(得分:2)

教科书是正确的。 (令人震惊。)

Func1在与func2不同的各个方面都有问题。它从堆中分配一个对象,而不考虑该对象的删除方式。然后它返回对新对象的引用,隐藏可能已经用于删除它的指针。没有效率增益,事实上Func1可能有点慢。在任何情况下,请背诵我:"避免早期优化。"

自标准模板库出现以来,许多月前,使用operator new几乎绝不是最佳选择。我最后一次使用operator new是ca. 2003年,我将指针包装在我们现在所知的unique_ptr中。在使用operator new之前,请阅读所有关于智能指针和RAII的所有知识。

答案 2 :(得分:1)

由于这是教科书示例,您应该考虑它的上下文以及它想要准确显示的内容(其目标是在您或使用安全编程模式时最小化内存使用量?!)。但有两个暗示

  1. 当您使用new运算符分配内存时,必须使用delete取消分配。该代码在heapstr处有func1的内存泄漏。
  2. 同样在更现实的项目中,在方法之间共享对象是不安全的。它的管理(即当前修改了这个或谁负责在不再需要对象时解除其内存)变得很难。
  3. PS:我没有C ++ 17,但它也优化了以下内容。有关详细信息,请阅读@BoPersson评论。

    PS:堆栈分配更快,但在您的示例中,您在func2返回时有一个复制操作。 在你的例子中,@ Jive Dadson说由于编译器优化没有区别,但在一般情况下,假设以下代码

    #include <iostream>
    #include <string>
    using namespace std;
    
    
    string& func1(const string &str) {
        string* heapstr = new string();
        cout << "func1 " << heapstr << endl;
        for (int i = 0; i < str.size(); ++i) {
            *heapstr += str[i];
        }
        return *heapstr;
    }
    
    string func2(const string &str) {
        string heapstr;
        for (int i = 0; i < str.size(); ++i) {
            heapstr += str[i];
        }
        cout << &heapstr << endl;
        return heapstr;
    }
    
    int main() {
        string a = func1("aaa");
        string b = func2("aaa");
        cout << "main " << a << endl;
    }
    

    PS :(正如@Jive Dadson所说,你的例子没有区别,但在我的例子中)如果我们将性能定义为运行时,可能是func1。此外,如果我们将性能定义为内存使用量,则为func1。如果我们将性能定义为良好的编程模式,则为func2。完全func2是更优选的。