【问题标题】:Class design: how to return a shared_ptr: reference or copy类设计:如何返回 shared_ptr:引用或复制
【发布时间】:2013-01-30 15:25:23
【问题描述】:

这是场景:我有一个名为 Program 的类,它包含三个 shared_ptr:顶点、几何和片段着色器。当 Shader 对象被构造时,它会使用 glCreateShader 创建着色器,并且还会编译它。

Shader denstructor 会自动调用 glDeleteShader。所以问题是,如果我执行以下操作:

  1. 创建一个着色器对象;
  2. 复制它;
  3. 销毁副本。

原始副本也会失效,因为当副本被销毁时,它会调用 glDeleteShader。我相信这是一个设计问题。

所以我只使用指针就避免了这个问题。现在 Program 类拥有着色器。我创建了一个将 shared_ptr 返回到顶点、几何和片段着色器对象的方法。我的疑问是:我应该像这样返回 shared_ptr:

const shared_ptr<Shader>& getVertex_shader_ptr() const
{
    return vertex_shader_ptr;
}

或者像这样:

shared_ptr<Shader> getVertex_shader_ptr() const
{
    return vertex_shader_ptr;
}

我担心我上面描述的问题会再次发生:shared_ptr 被释放并且它使 OpenGL 着色器无效。

【问题讨论】:

标签: c++ memory-management shared-ptr opengl-2.0


【解决方案1】:

除非您的着色器为 NULL 有效,否则您应该只返回对它的引用:

const Shader& getVertex_shader() const
{
    return vertex_shader;
}

如果你的着色器为 NULL 是有效的,但只有你的程序负责删除它,只需返回一个指针:

const Shader* getVertex_shader_ptr() const ...

共享指针的语义适用于着色器没有明确所有权的情况。如果是这种情况,则按值返回。语义是程序的两个部分都对未清理的对象感兴趣,并且它们都需要一种方法来保持它的活力。

shared_ptr<Shader> getVertex_shader_ptr() const ...

【讨论】:

  • 关于“没有明确的所有权”——这不是shared_ptr 的目的,但不幸的是,它似乎经常在使用它们时出现!我总是确保对象的所有权是明确的,并使用共享/弱指针来管理/监视它们的生命周期。也许在这种情况下,着色器需要在使用完毕之前保持活动状态,这可能是在程序所有者被销毁之后?我们只是不知道:)
  • +1 OP 似乎根本不需要shared_ptr,他只需要一个复制构造函数和赋值运算符。
  • 第二种情况:shader可能为null,但只是程序可能会删除它。
  • @Bill shared_ptr 的整点暗示按值返回。如果引用返回有效,则使用shared_ptr 没有意义;只需返回对实际对象的引用。
  • @RamyAlZuhouri 好的,在这种情况下,您实际上并不需要 shared_ptrs,除非您需要某种方法来验证返回的着色器在一段时间后仍然有效,在这种情况下您可能想要存储给它一个weak_ptr。如果不是,您需要复制/移动构造函数/赋值运算符,并通过 const 原始指针返回。
【解决方案2】:

无论哪种方式都可以。

它是一个成员,因此返回对它的引用是安全的,只要调用者不通过引用持有它,然后销毁Program

如果你是通过非常量引用返回的,那么调用者可以在你的指针上调用reset,如果不存在指针的其他副本,它将调用glDeleteShader。尽管如此,它会按照你的意愿行事 - Shader 只有在不再存在对它的引用时才会被销毁。

我会亲自按价值返回 shared_ptr,但这只是个人喜好。

编辑:假设您的意思是您在复制Program(不是Shader)时担心正确性,也不要担心;两个Programs 将拥有相同的Shader 的shared_ptrs,直到Programs 都被销毁。这可能是也可能不是您想要的(您可能希望在副本上分配一个完全独立的 Shader),但它是安全的。

【讨论】:

  • 至于类的设计,你觉得好还是我做的太复杂了?
  • 我会说你没有向我们展示足够多的课程设计让我们确定这一点。
【解决方案3】:

我更愿意按值返回。如果您通过引用返回,您可能会遇到对 shared_ptr 的悬空引用,如果在某个时候实例被销毁并且某些变量仍然持有对 shared_ptr 的引用。

这种情况正是智能指针应该避免的,但它们的引用计数只有在你避免通过复制返回它们时才能安全地工作。这将使在此处使用 shared_ptr 的全部意义无效。

【讨论】:

    【解决方案4】:

    在不太了解底层共享指针问题的情况下,我发现 Thiago Macieira(可能是 Qt/Trolltech 员工)的这篇文章非常有助于更好地了解是返回值还是返回 const&amp;:http://lists.trolltech.com/qt-interest/2007-11/thread00209-0.html

    引用那篇文章,这是从const 函数返回值的核心参数:

    原因:

    T at(const Key& k) const;
    

    代替:

    const T &at(const Key &k) const;
    

    是因为它在 Qt 中无处不在。我们不返回 refs-to-const 任何地方。为了 要返回一个 ref-to-const,我们需要一个变量存在于某处, 它要求类的内部结构。通过返回一个 值,我们可以在内部做任何我们想做的事情。

    但我不确定,如果这适用于您的问题...(仍在学习自己;))

    【讨论】:

    • 好点:如果它是通过引用返回的,它可能会被改变,所以它不再是 const。
    • “......它要求类的内部结构”是重要的一点。 “...因为在 Qt 中到处都是这样”是开始解释的糟糕方式:)
    • @RamyAlZuhouri :请注意,引用返回函数返回一个const T &amp;(或者在我个人更喜欢的符号中,一个T const &amp;),即引用到常量;这意味着虽然它是一个引用,但无论处理返回的值都不能改变它。
    猜你喜欢
    • 1970-01-01
    • 2015-06-24
    • 2012-05-25
    • 2019-06-13
    • 2011-02-10
    • 2021-11-13
    • 2023-03-20
    • 2014-09-29
    • 1970-01-01
    相关资源
    最近更新 更多