如果我有一个基本抽象类,其中一个函数将自己的类作为参数:
class Component abstract
{
public:
virtual bool Method(Component& other) = 0;
};
我有一个子类,它覆盖并重载了这个函数,以接受 its class 的参数:
class DerivedComponent : public Component
{
public:
virtual bool Method(Component& other) override;
virtual bool Method(DerivedComponent& other);
};
我将这些作为另一个类的组件,如下所示:
class Foo
{
private:
Component* m_component;
public:
Foo()
{
m_component = new DerivedComponent();
}
Component* GetComponent()
{
return m_component;
}
};
然后调用组件的方法,传入Foo
的组件:
Foo foo1 = Foo();
Foo foo2 = Foo();
foo1.GetComponent()->Method(*foo2.GetComponent());
为什么它调用DerivedComponent
的第一个非重载方法Method(Component& other)
,而不是Method(DerivedComponent& other)
?
2条答案
按热度按时间htrmnn0y1#
编译器不知道你使用的是派生类型,也不会自动向上转换指向该类型的指针。
GetComponent
返回一个Component*
。它可以是 * 任何 * 子类,而不仅仅是DerivedComponent*
。如果你知道
Component*
实际上是一个DerivedComponent*
,你可以自己显式地转换它:如果你不知道,并且RTTI被启用,那么你可以使用dynamic_cast:
从上面的混乱中可以看出,这并不是特别理想。通常情况下,这表明您的设计中存在一个更根本的问题,您可能需要重新考虑。
至少,如果你需要一个
DerivedComponent
的特殊行为,你可以把dynamic_cast的东西移到你的实现中,而不是期望调用者去做:现在,上面的代码将按照您的预期运行,如下所示:
deikduxw2#
DerivedComponent
声明了Method
的两个重载:一个需要Component &
,另一个需要DerivedComponent &
。但是重载总是静态解析的。也就是说,编译器必须在编译时决定哪个重载函数将被调用。由于该解析发生在编译时,因此它基于编译时可用的信息:指针的静态类型/引用本身,而不是它所指向/引用的对象的动态类型。
不过,有一种相当简单的方法来处理它--访问者模式(或者至少是它的一个近似变体)。
为了实现它,我们添加了另一个虚函数当我们使用
Method
时,它会对传入的对象调用Dispatch
。(通过引用)。因此,我们可以得到依赖于进行调用的对象和传递的对象的行为。这里是您的演示的一个稍微扩展的版本,显示了调用对象的虚拟行为,和传递的对象:结果:
当然,在真实的使用中,我们还需要处理一些其他事情--
Foo
可能需要一个dtor来销毁Component
。(或派生)对象。它可能还需要做一些关于复制、移动和赋值的事情(但它究竟应该做什么是一个单独的问题)。并且可能会通过指向基类的指针/引用销毁派生对象,Component
需要一个虚拟dtor
。(可能还有一些事情我暂时没有想到--但是我们正在使用继承,所以我们需要做所有常见的继承“事情”)。