C ++多态性和指针向量的问题
-
20-09-2019 - |
题
考虑以下示例代码:
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
在客户端代码中的s,但这感觉是错误的,因为客户不必知道 Foo
的存在。
我也可以定义一个 get()
从中检索对象的成员 d_foos
并把它们铸造出来,但这感觉很笨拙。最好是,我只想返回D_Foos作为一个 vector<shared_ptr<Bar> > &
, ,但我似乎不能这样做。
也可能是我的设计完全是错误的。但是,这似乎是最自然的解决方案,因为 Bar
是专业 Foo
和 BarCollection
是专业 FooCollection
他们共享功能。
您能建议实施的好解决方案吗 getBars
在 BarCollection
还是更好的设计替代方案?
编辑:
原来我的设计确实很糟糕。尽管需要所有FoooCollection的功能,但Barcollection并不是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.
};
感谢所有出色的建议和示例!
解决方案
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> {
};
其他提示
我建议从您的容器类中曝光迭代器,而不是成员容器。这样,什么是容器类型都无关紧要。
问题是您正在尝试以一种行不通的方式混合两种不同的独立多种多态性。模板的编译时,类型安全的多态性不允许您将基本类型替换为派生类型。 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的矢量可能完全很好。酒吧的用户已经必须对FOO有充分的了解,因为包括bar.h必须通过bar.h获得foo.h。原因是,要从foo中继承Bar,它必须通过FOO.H对班级有充分的了解。我建议而不是使用上面的解决方案,如果可以使FOO(或超级类Foo)一个接口类,然后将指针的向量传递到该接口类。这是一种通常看到的模式,不会引起我提出的这种不可思议的解决方案的眉毛:)。然后,您可能有理由。无论哪种方式,祝你好运。
问题是,你为什么要这样做?您假设,如果您为用户提供了一系列限制的指针,则只有条上只有条,因此内部将指针存储在集合中的指针是没有道理的。如果您将不同的FOO子类型存储在FOO的指针集合中,则不能将其作为钢筋集合的集合返回,因为并非所有物体都有bar。在第一种情况下,(您知道您只有条)您应该使用上述建议的模板方法。否则,您必须重新考虑您真正想要的。
您不能用在foo / bar上模板的集合代替吗?
class Collection<T> {
protected:
vector<shared_ptr<T> > d_foos;
};
typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;
你有特殊需要 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
, ,各种各样的丑陋的事情将会发生。
因此,通常您想避免这种情况,并且没有彼此派生的收集课程。也可以看看 问题 关于 铸造容器 关于此主题的更多答案的派生类型...
首先,让我们谈谈 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
, ,如前所述,您会遇到麻烦,因为您可以在指针向量中介绍其他元素 Bar
那些。
不过,我不会扩展这一点,因为这超出了手头的问题,我认为我们(正如那些试图回答的人)应该限制自己。
您不能通过参考,但是可以通过 View
.
基本上, View
是一个充当的新物体 Proxy
到旧的。使用相对容易使用 Boost。术语 从示例。
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:
请考虑重构。那里有很好的建议。