请考虑以下示例代码:
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
的{{1}}指针,该FooCollection
在Foo
中声明。我现在想要向客户端代码公开指向Bars的指针集合。我可以让客户端代码访问指向Bar
的指针向量并将其转换为客户端代码中指向Foo
的指针,但这感觉不对,因为客户端不必知道关于get()
的存在。
我还可以定义一个d_foos
成员从vector<shared_ptr<Bar> > &
中检索对象并对其进行转换,但这感觉非常笨拙。我希望将d_foos作为Bar
返回,但我似乎无法做到这一点。
也可能是我的设计完全错了。这似乎是最自然的解决方案,因为Foo
是BarCollection
的特化,FooCollection
是getBars
的特化,它们共享功能。
您能否建议在BarCollection
或更好的设计方案中实施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.
};
的好解决方案?
修改
原来我的设计确实很糟糕。尽管要求所有的FooCollection功能,但BarCollection不是FooCollection。我现在的解决方案基于以下答案 - 现在更清洁 - 现在:
{{1}}
感谢所有出色的建议和例子!
答案 0 :(得分:3)
问题是你试图以一种不起作用的方式混合和匹配两种不同的,非常独立的多态性。模板的编译时类型安全多态性将不允许您将基类型替换为派生类型。 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的超类)成为接口类并传递指向该接口类的指针向量。这是一个非常常见的模式,并不会引起我想出的这个难以理解的解决方案的眉毛:)。然后你可能有你的理由。祝你好运。
答案 1 :(得分:3)
我建议从容器类中公开迭代器,而不是成员容器。这样,容器类型是什么并不重要。
答案 2 :(得分:2)
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> {
};
答案 3 :(得分:2)
问题是,你为什么要这样做?如果你给用户一个指向Bar的指针集合,你可以假设它中只有Bars,所以内部将指针集中存储到Foo是没有意义的。如果在指向Foo的指针集合中存储Foo的不同子类型,则不能将其作为指向Bar的指针集合返回,因为并非所有对象都是Bars。 在第一种情况下,(你知道你只有条形图)你应该使用如上所述的模板化方法。 否则,你必须重新思考,你真正想要的。
答案 4 :(得分:1)
你能不能用在Foo / Bar上模板化的Collection来替换它吗?这样的事情
class Collection<T> {
protected:
vector<shared_ptr<T> > d_foos;
};
typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;
答案 5 :(得分:1)
您是否特别需要BarCollection
来自FooCollection
?由于BarCollection
通常不是 FooCollection
,因此FooCollection
通常可以使用BarCollection
完成很多事情。 }}。例如:
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
对象添加到应该是BarCollection
的对象中。如果BarCollection
尝试访问此新添加的元素并期望它是Bar
,则会发生各种丑陋的事情。
所以通常你想要避免这种情况,并且没有相互派生的集合类。有关questions派生类型的信息,另请参阅casting containers以获取有关此主题的更多答案...
答案 6 :(得分:1)
首先,我们来谈谈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
(或从它派生),那么你得到一个空指针。
回到你的问题:BAD CODE。
BarCollection
不是FooCollection
,如上所述,因为您可以在Bar
个指针的向量中引入其他元素,因此您遇到了麻烦。
我不会继续这样做,因为这超出了手头的问题,我认为我们(正如那些试图回答的人)应该克制自己。
您无法传递参考,但可以传递View
。
基本上,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
shared_ptr
对象很简单,并允许根据需要执行dynamic_cast
。获取reference
到ORIGINAL
shared_ptr
但被解释为所需类型...实际上并不是我希望在代码中看到的。
注意强>:
可能有一种方法比使用Boost.Fusion transform_view类更好,但我无法理解。
特别是,使用transform_view
我可以获得shared_ptr<Bar>
但是当我取消引用我的迭代器时我无法获得shared_ptr<Bar>&
,这很烦人,因为返回引用的唯一用途基础vector
(而不是const_reference
)实际上是修改vector
及其包含的对象的结构。
注2 :
请考虑重构。那里有很好的建议。