代码之家  ›  专栏  ›  技术社区  ›  TC.

具有多态性和指针向量的C++问题

  •  3
  • TC.  · 技术社区  · 16 年前

    考虑下面的示例代码:

    class Foo
    {
    };
    
    class Bar : public Foo
    {
    };
    
    class FooCollection
    {
    protected:
        vector<shared_ptr<Foo> > d_foos;
    };
    
    class BarCollection : public FooCollection
    {
    public:
        vector<shared_ptr<Bar> > &getBars()
        {
            // return d_foos won't do here...
        }
    };
    

    在我当前的项目中,我遇到了这样的问题。客户端代码使用 BarCollection Bars 在里面 d_foos FooCollection . 现在我想将指向条的指针集合公开给客户机代码。我可以让客户端代码访问指向的指针向量 Foo s,并将其转换为指向 Bar 客户机代码中有,但这感觉不对,因为客户机不必知道 生命的存在。

    我还可以定义一个 get() 并投掷它们,但这感觉相当笨拙。最好,我只想把d_foos作为 vector<shared_ptr<Bar> > & ,但我似乎不能这样做。

    酒吧 是一个专业化的 酒吧收藏 是一个专业化的 食品收集 它们共享功能。

    你能建议好的解决方案吗 getBars 在里面 酒吧收藏

    编辑:

    原来我的设计真的很糟糕。BarCollection不是FooCollection,尽管它需要FooCollection的所有功能。我目前基于以下答案的解决方案(更干净)是:

    class Foo
    {
    };
    
    class Bar : public Foo
    {
    };
    
    template<class T>
    class Collection
    {
        vector<shared_ptr<T> > d_items;
    };
    
    typedef Collection<Foo> FooCollection;
    
    class BarCollection : public Collection<Bar>
    {
        // Additional stuff here.
    };
    

    感谢所有优秀的建议和例子!

    7 回复  |  直到 16 年前
        1
  •  3
  •   Chris Bednarski    16 年前

    我建议公开容器类中的迭代器,而不是成员容器。这样,容器类型就不重要了。

        2
  •  3
  •   James McNellis    16 年前

    问题是,您试图以一种不起作用的方式混合和匹配两种不同的、几乎独立的多态性。模板的编译时类型安全多态性不允许用基类型替换派生类型。C++的模板系统不与

    class<Foo>
    

    class<Bar>
    

    一个建议可能是创建一个Foo派生的适配器,该适配器将向下转换到正确的类:

     template <class derived, class base>
     class DowncastContainerAdapter
     {
     private:
         std::vector< boost::shared_ptr<base> >::iterator curr;
         std::vector< boost::shared_ptr<base> >::const_iterator end;
     public:
         DowncastContainerAdapter(/*setup curr & end iterators*/)
         {
             // assert derived actually is derived from base
         }
    
         boost::shared_ptr<derived> GetNext()
         {
             // increment iterator
             ++curr;
             return dynamic_cast<base>(*curr);
         }
    
         bool IsEnd()
         {
             return (curr == end);
         }
     };
    

    请注意,此类将与迭代器有相同的问题,对向量的操作可能会使此类无效。

    另一种想法

    您可能没有意识到这一点,但是只返回一个Foo向量就可以了。Bar的用户必须已经完全了解Foo,因为通过包含Bar.h,他们必须通过Bar.h获得Foo.h。原因是为了让Bar从Foo继承,它必须通过Foo.h完全了解类。如果可能的话,我建议不要使用上面的解决方案,而是将Foo(或Foo的一个超类)作为接口类,并传递指向该接口类的指针向量。这是一个非常常见的模式,不会引起我提出的这个古怪的解决方案可能会引起的注意:)。那么你可能又有你的理由了。祝你好运。

        3
  •  2
  •   Alexey Malistov    16 年前
    template<class T>
    class MyContainer {
      vector<shared_ptr<T> > d_foos;
    public:
      vector<shared_ptr<T> > & getVector();
    };
    
    class FooCollection : public MyContainer<Foo> {
    };
    
    class BarCollection : public MyContainer<Bar> {
    };
    
        4
  •  2
  •   Björn Pollex    16 年前

    问题是,你为什么要这么做?如果给用户一个指向Bar的指针集合,则假定其中只有Bar,因此在内部将指针存储在指向Foo的集合中是没有意义的。如果在指向Foo的指针集合中存储了Foo的不同子类型,则不能将其作为指向Bar的指针集合返回,因为其中并非所有对象都是Bar。 在第一种情况下,(您知道您只有条形图),您应该使用上面建议的模板化方法。 否则,你必须重新思考,你真正想要的是什么。

        5
  •  1
  •   Jeff Foster    16 年前

    class Collection<T> {
    protected:
        vector<shared_ptr<T> > d_foos;
    };
    
    typedef Collection<Foo> FooCollection;
    typedef Collection<Bar> BarCollection;
    
        6
  •  1
  •   Community Mohan Dere    9 年前

    你有特殊需要吗 BarCollection FooCollection 酒吧收藏 不是 A. 食品收集 ,通常很多事情都可以通过 食品收集 不应使用 酒吧收藏

    BarCollection *bc = new BarCollection();
    FooCollection *fc = bc; // They are derived from each other to be able to do this
    fc->addFoo(Foo());      // Of course we can add a Foo to a FooCollection
    

    现在我们添加了一个 Foo 反对所谓的 酒吧收藏 . 如果 酒吧收藏 Bar ,各种丑恶的事情都会发生。

    questions 关于 casting containers 有关此主题的更多答案,请参阅派生类型的列表。。。

        7
  •  1
  •   Matthieu M.    16 年前

    首先,让我们谈谈 shared_ptr . 你是否知道: boost::detail::dynamic_cast_tag ?

    shared_ptr<Foo> fooPtr(new Bar());
    shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());
    

    这是一个非常方便的方法。在封面下,它只是执行一个 dynamic_cast Bar (或从中派生),则会得到一个空指针。

    BarCollection 不是一个 FooCollection 酒吧

    您不能传递引用,但可以传递引用 View

    基本上是 看法 Proxy 给老一个。它相对容易使用 Boost.Iterators 从例子来看。

    class VectorView
    {
      typedef std::vector< std::shared_ptr<Foo> > base_type;
    
    public:
      typedef Bar value_type;
      // all the cluttering
    
      class iterator: boost::iterator::iterator_adaptor<
        iterator,
        typename base_type::iterator,
        std::shared_ptr<Bar>
      >
      {
        typename iterator_adaptor::reference dereference() const
        {
          // If you have a heart weakness, you'd better stop here...
          return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
        }
      };
    
      // idem for const_iterator
    
      // On to the method forwarding
      iterator begin() { return iterator(m_reference.begin()); }
    
    private:
      base_type& m_reference;
    }; // class VectorView
    

    这里真正的问题当然是 reference 一点得到 NEW 共享ptr 对象很容易,并且允许执行 动态浇铸 根据需要。得到 参考 ORIGINAL 共享ptr

    :
    使用Boost.Fusion可能会有一种比这更好的方法 transform_view 上课,但我想不出来。

    特别是,使用 transform_view 我可以 shared_ptr<Bar> 但是我找不到一个 shared_ptr<Bar>& 当我取消引用迭代器时,这很烦人,因为只使用返回对底层对象的引用 vector (而不是 const_reference 矢量 以及它包含的对象。

    附注2 :
    请考虑重构。那里有很好的建议。