函数是不是应该返回一个“新”对象

Posted

技术标签:

【中文标题】函数是不是应该返回一个“新”对象【英文标题】:Should a function return a "new" object函数是否应该返回一个“新”对象 【发布时间】:2017-09-10 23:44:08 【问题描述】:

函数是否应该返回指向在堆上分配的内存的指针?

也就是说,以下哪种方法更“正确”?

// Method #1
Object* getObject1() 
    return new Object();


// Method #2
std::shared_ptr<Object> getObject2() 
    return std::make_shared<Object>();


int main() 
    // Usage of method #1
    Object* o1 = getObject1();
    o1->doSomething();
    delete o1; // object has to be deleted by user

    // Usage of method #2
    std::shared_ptr<Object>& o2 getObject2(); // has to be kept in shared_ptr
    o2.get()->doSomething();
    // object is deleted when o2 destructs

我想第一种方法可能更快,但第二种方法不需要用户删除对象。

【问题讨论】:

为什么第一种方法应该“更快”?您是否试图在紧密循环中每秒调用一百万次? 第一个解决方案在现代 c++ 中没有一席之地。至少,它应该创建并返回一个std::unique_ptr&lt;Object&gt; 还有方法3,返回std::unique_ptr&lt;Object&gt;。如果不确定,请这样做。没有开销,您始终可以将唯一指针转换为共享指针(在进程中使用它)。此外,您不能忘记删除unique_ptr Herb Sutter - 对象工厂:herbsutter.com/2013/05/30/gotw-90-solution-factories @ChristianHackl,为什么不呢? 【参考方案1】:

这两种选择都不是很好。

默认情况下,您可以在 C++ 中做的最好的事情是:

Object getObject1() 
    return Object();

如果您真的必须使用动态分配,那么您的首选应该是按值返回std::unique_ptr

std::unique_ptr<Object> getObject1() 
    return std::make_unique<Object>();

请参阅 "GotW #90 Solution: Factories" by Herb Sutter 以获取有关该主题的精彩文章。除其他外,萨特说:

返回 unique_ptr 表示返回唯一所有权,即 纯“源”工厂函数的规范。 unique_ptr 在效率上无可匹敌 - 移动一个与移动/复制原始指针一样便宜。

至于你的具体问题:

我想第一种方法可能更快,

std::shared_ptr 涉及锁定以保证线程安全,因此它可能会降低性能。但是,这并不重要。动态分配通常总是比替代方案慢,因此您通常不想在微观性能重要的地方使用它(例如在紧密循环中或在每秒执行多次的代码的某些部分中)。 C++ 不鼓励您在免费商店中创建许多小对象。所以像这样的函数一开始就不应该成为瓶颈。

【讨论】:

这个问题我想了很久。到目前为止,我发现的所有解决方案在我看来都不理想。我希望它从使用的角度来看是简洁的,并且从提供的角度来看是可扩展的,但还不能找到一个完整的解决方案。在消费方面,我希望它看起来像 auto obj = ClassName::Create>(....) 或者你可以做 auto obj = ClassName::Create>(. ...)。在提供方面,您实现该分配器并将其与 unique_ptr 或 shared_ptr 或 local_val 或其他相关联。我还不能创建这样的模板。【参考方案2】:

他们都是正确的。但是,为第一种情况创建一个“删除”函数是一个好习惯...

为什么?

因为您有一个负责分配对象的“接口”,那么您应该有另一对执行解除分配,一旦此函数的使用者使用您的接口,就不需要知道new正在内部发生......它可能是 malloc,例如,或其他任何东西......所以这对删除器函数将实现 deletefree 或任何其他你必须正确释放的东西。

【讨论】:

【参考方案3】:

在这两个中,第二个将是首选。裸指针应该永远是你最后的选择。但理想情况下,您只需按值返回 Object。如果做不到这一点,unique_ptr 会比shared_ptr 更好,除非您确实需要共享所有权。

我想第一种方法可能更快。

shared_ptr 可能是这样。但是使用unique_ptr,编译器可以进行优化。手动删除的风险没有任何好处可以补偿您。

【讨论】:

+1 for “理想情况下,您只需按值返回 Object。”。不太熟悉 C++ 的人有时不会意识到按值返回对象通常是 optimised to eliminate temporary objects,因此比返回指向堆分配对象的指针更有效。 @BenCottrell 特别是在正确使用移动语义的情况下。它有时更有效,通常成本相等,而且很少更昂贵(假设设计合理的类)。即使在那些成本更高的罕见情况下,获得简单直观的值语义通常仍然值得。 @DavidSchwartz 你说:“裸指针应该永远是你的最后选择”,肯定有返回Object* 更好的情况。例如,当您不希望用户获得所有权时。 @DutChen18 我会先尝试其他所有方法,例如返回参考。 @DutChen18 在这种情况下,我更倾向于返回Object&amp;,如果我没有什么可返回的则抛出异常(而不是使用Object* 来返回nullptr ,因为我在返回 nullptr 时看到的最常见用法通常看起来像是一个隐身的错误返回代码)

以上是关于函数是不是应该返回一个“新”对象的主要内容,如果未能解决你的问题,请参考以下文章

函数的输入对象将被返回覆盖

返回对象和返回引用

函数返回一个指向对象的指针,在这种情况下我应该返回啥?

面向对象编程

expo-sqlite 返回 undefined 不是 excuteSql 函数中的对象

new实例化函数的过程