【问题标题】:How do I make an abstract class properly return a concrete instance of another abstract class?如何使抽象类正确返回另一个抽象类的具体实例?
【发布时间】:2011-03-20 15:07:54
【问题描述】:

我最近重新设计了我自己的一个库,以尝试将接口与实现分离。我遇到了一个旨在返回另一个类的实例的类的最终问题。

在接口定义中,我做了类似的事情

struct IFoo
{
    virtual const IBar& getBar() = 0;
}

然后在具体的 Foo getBar 看起来像

const IBar& Foo::getBar()
{
    Bar ret = Bar();
    return ret;
}

问题是 ret 在 getBar 完成后立即被删除,当复制构造函数尝试像这样使用 Bar 时会导致严重崩溃

const Bar myBar = myFoo.getBar();

我一直在阅读各种东西,我知道通过引用返回是不受欢迎的,但我没有看到任何其他方式(我不想返回 Bar* 因为我不想手动删除返回值)。

抽象类返回从另一个抽象类派生的具体类的实例的正确方法是什么(如果有的话)?

注意我确实看到了这个解决方案:returning an abstract class from a function 但我不想使返回值静态和松散的线程安全。

【问题讨论】:

    标签: c++ interface abstract-class


    【解决方案1】:

    使用智能指针。 这些是不再使用时被删除的指针(参见例如http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/smart_ptr.htm)。

    【讨论】:

    • 哈哈,返回智能指针使得使用返回类型有点困难,但我认为这应该很好,谢谢。
    • 我认为在到处使用智能指针之前,您应该考虑以正确的方式使用它们。这个博客是关于该主题以及它们如何容易被过度使用的有趣读物:bureau14.fr/blogea/index.php/2009/08/…
    • 在此处跟进:在我的评论中,我注意到智能指针是一个不错的选择,但会使处理对象变得更加困难。应该注意的是,您可以使用复制构造函数和 operator= 定义来消除处理这些对象的几乎所有不便。在我的项目中,我现在为具体类定义了两个复制构造函数和两个 operator=。一个取 const Object&,另一个取 const boost::shared_ptr。效果很好。
    【解决方案2】:

    也可以按值返回对象。

    一些编译器提供返回值优化,在返回对象时优化掉副本。

    编辑:
    对不起。我浏览了这个问题,不知何故错过了涉及继承的事实。假设 getBar() 可以返回各种 种类 的 IBar,并返回一个 IBar 指针,这很有意义。

    通过返回一个指向基址的指针,具体对象保持不变。避免使用slicing problem,原始的vtbl 指针可用于进行虚函数调用。 (正如您在评论中指出的那样) 返回一个 抽象类 的实例也是不可能的。

    我建议您不要返回原始指针,而是返回 shared_ptr<IBar> 以简化内存管理。

    const shared_ptr<IBar> Foo::getBar()
    {
        shared_ptr<IBar> ret(new Bar());
        return ret;
    }
    

    那就这样用吧:

    shared_ptr<IBar> pIBar(foo.getBar());
    pIBar->myVirtualFunction();
    

    shared_ptr 是 C++0x 中最常用的智能指针类型。如果您有足够新的编译器,它将位于 std 命名空间中。较旧的编译器可能将它放在 tr1 命名空间中,它也是 boost 的一部分。

    【讨论】:

    • 是的,太糟糕了,我返回一个抽象类类型,编译器抱怨在没有 & 或 * 的情况下实例化抽象类:(
    【解决方案3】:

    您正在返回对局部变量的引用。一旦函数返回引用,堆栈就会被弹出并且 Bar 对象不再存在。

    编辑:我没有阅读全文。您可能需要使用智能指针。

    实际上,有什么理由需要返回基类引用吗?您可以通过返回具体类型本身的对象来避免任何智能指针混乱,因为 C++ 允许协变返回类型。

    【讨论】:

    • 我也想过,但问题是虚拟方法必须使用&或*,我不能声明一个返回抽象类的纯虚拟方法,这对我来说似乎很愚蠢,但是,嘿,也许有一些我不知道的东西=/
    • 我认为函数返回类型的协方差只有在返回对象引用和指针时才有效。
    【解决方案4】:

    由于您想将返回对象的所有权转移给调用者,调用者将不得不销毁该对象。换句话说,返回 IBar * 是您最好的选择。如果您担心必须手动调用 delete,您应该考虑使用智能指针包,例如boost::shared_ptr.

    【讨论】:

    • 我认为他似乎不想转让所有权,因为他不想自己删除实例。如果您不想删除它,则不应拥有它。 (即使它是一个智能指针)。
    • 所以对原问题有两种可能的解释:1)作者担心调用者将不得不调用delete(例如所有权转移)2)作者担心类本身会必须调用删除。这意味着指针被存储为类的成员。但是,如果这是真的,那么是什么阻止了作者使实例成为类的成员并返回引用呢?这就是我假设所有权应该转让的原因。
    【解决方案5】:

    如果您不想处理删除操作,则必须使用 SmartPointers。 在 C++ 中,这是让对象在被占用时“删除自身”的唯一方法。

    http://en.wikipedia.org/wiki/Smart_pointer

    【讨论】:

      【解决方案6】:

      当您的堆栈被移除时,已在堆栈上创建的对象将被销毁。函数退出时堆栈被移除。

      请尝试以下方法:

      
      struct Foo : public IFoo
      {
        Bar m_Bar;
      public:
        virtual const IBar& getBar() 
        { 
          return m_Bar;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-13
        • 2013-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多