通过值或指向C ++上的动态分配返回一个大型struct对象

时间:2013-03-01 16:02:09

标签: c++ memory-management stack heap

这是我的问题。我有一个以struct为输入的函数,分配一个新的struct然后返回它。 struct具有以下内容

struct Data {
std::vector<custom_type> vector_vars;
std::string key;
std::map<std::string, Data*> index;
};

vector_vars的大小为500-1000。如果相关的是this class,则为custom_type。 index帮助我按Data访问不同的key结构。这就是功能。

Data func(Data inputData) {

 Data outputData;
//compare values with inputData
//change some values in Data struct
 return outputData
}
  1. 我是否使用堆栈分配并将其返回以便将其复制到RHS。这是否可取,因为复制可能会产生很多开销?

  2. 我可以在函数内返回在堆栈上分配的struct的引用/指针吗?

  3. 建议在此使用动态分配(可能与std::shared_ptr一起使用)以提高效率吗?

4 个答案:

答案 0 :(得分:14)

  1. 按值返回对象。复制省略是一种优化,标准允许编译器删除不必要的副本。在C ++ 03中,编译器几乎肯定会删除该副本。在C ++ 11中,副本将首先被视为移动(它将反过来移动其内容),甚至可能被删除。

      

    在具有类返回类型的函数中的return语句中,当表达式是具有相同cv-unqualified的非易失性自动对象(函数或catch子句参数除外)的名称时键入函数返回类型,通过将自动对象直接构造为函数的返回值,可以省略复制/移动操作

    具体来说,当此标准导致复制省略时,通常称为命名返回值优化,return value optimization的形式。

    因此,这里最惯用且性能最友好的选择是按价值返回。当然,如果您度量,即使在按值返回优化的情况下,那么您可以考虑动态分配(参见第3点)。

    < / LI>
  2. 不,您不应该返回指向局部变量的引用或指针。具有自动存储持续时间的对象(您在堆栈上称为分配的对象)将不再存在。引用或指针将悬空。以任何有意义的方式使用它将导致未定义的行为。

  3. 不,如上所述,建议按值返回。但是,如果您确实需要,可以动态分配Data并通过智能指针返回它。这里首选的智能指针是std::unique_ptr,因为你的功能是将所有权传递给调用函数(不共享所有权)。

答案 1 :(得分:2)

  1. 实际上,这里可能会发生两个快速操作中的一个:outputData可以移动到返回值,或者可以省略此移动。在任何情况下都不会有任何昂贵的副本。 inputData的副本似乎没有必要,也许您应该将其作为Data const& inputData传递。
  2. 您可以这样做,但会导致未定义的行为。
  3. 否。动态分配可能更不安全,更慢,更混乱,而且通常更糟。

答案 2 :(得分:2)

Data func1(Data inputData) {
    Data outputData;
    return outputData
}

按值Data获取对象,并创建另一个Data实例,然后按值返回。因此,不仅在返回outputData时,而且在收到inputData时创建副本。在按值返回时不要担心性能,有一些机制可以处理它(值得一提的是 copy elision 和可能的返回值优化)。我建议您通过引用inputDataData func1(const Data& inputData)

  

这是否可取,因为复制可能会产生大量开销?

经验法则:除非遇到性能问题,否则不要担心代码的性能。

  

我可以在函数中返回在堆栈上分配的struct的引用/指针吗?

不要在该函数范围内定义自动存储持续时间的对象返回引用/指针。一旦执行超出范围,对象就会被破坏,因此您将最终得到悬空指针无效引用),这将导致未定义的行为

  

建议使用动态分配来提高效率吗?

尽可能避免动态分配。不是因为效率,而是为了代码中的内存管理。关注RAII idiom并避免自己处理难看的内存管理。

答案 3 :(得分:1)

您可以重写func()以返回Data*而不是Data。当然,复制Data*要比复制Data要快得多。但在那之前:你关心吗?这个功能在程序初始化期间运行一次,占用挂钟时间的十分之一,或者您希望每帧调用一次,还是什么?

当您编写程序或优化现有程序时,您的目标应始终是使其“足够快”(并且“足够响应”,但现在不要介意)。 “足够快”的定义变化很大:在模拟中,您可能乐于每小时处理1TB的数据,而在视频游戏中,您需要每16毫秒向玩家显示一个新的图形帧。

那么:只有堆栈分配,你的程序“足够快”吗?如果是,请不要管它。如果不是,请改用动态分配。一般来说,返回指针而不是大结构是很好的C ++风格,但是在你还在学习时你不应该担心。让它工作,做对,然后快速。