从Base引用对象的派生类的调用成员

时间:2019-12-10 15:52:54

标签: c++ inheritance

我在SO上发现了许多类似的问题,但是它们的解决方案要么无效,要么略有不同(通常使用指针代替,我不想更改caller()签名!)。如果已经问过(并回答了),请告诉我,我将删除此问题。

我发现了一种叫做 visitor模式的东西,但我很好奇是否有一个更简单的解决方案。我尝试不使用virtual来做,因为我的课程还没有介绍。还是我在这里需要它?

我有以下代码:

class Base
{
public:
    void fun(); // cout << "Base"
};

class Derived : public Base
{
public:
    void fun(); // cout << "Derived"
};

void caller(Base &obj)   // must stay as it is
{
    // here i want to call Derived::fun()
    // obj.fun() prints 'Base' but I need 'Derived'
}

int main()
{
    // Base base;
    // caller(base);

    // EDIT:
    Derived derived;
    caller(derived);

}

Base &传递给函数caller()时,让caller()调用Derived :: fun()而不是Base :: fun()的最简单方法是什么?

如果有一个简单的答案向我指出正确的方向,将不胜感激,我已经在这上面停留了一段时间:)

我知道您不会在生产代码中这样做,这是关于学习C ++的

编辑:我将调用者函数参数更改为类型Derived而不是Base

2 个答案:

答案 0 :(得分:3)

不可能在Derived类对象上调用Base成员函数。

每个Derived都是Base,但是Base不是Derived。换句话说,Derived可能具有Base缺乏的一堆信息。 您可以在Base类对象上调用Derived成员函数,但不能相反

换句话说...

class Base {
    public: void b_thing() { }
};

class Derived : public Base {
    public: void d_thing() { }
};

int main() {
    Base b;
    Derived d;

    d.b_thing(); // Okay!
    b.d_thing(); // Bad :(
}

但是,您可以有一个引用Base对象的Derived引用:

Base &b_ref = d;

由于这实际上是一个Derived对象,因此可以通过多态调用它调用Derived成员函数(在virtual中被声明为Base):

class Base {
    public: virtual int thing() { return 0; }
};

class Derived : public Base {
    public: int thing() { return 1; }
};

int caller(Base &obj)
{
    return obj.thing(); // Returns 1 if obj actually references a Derived object
}

int main() {
    Base b;
    Derived d;

    caller(d); // 1
    caller(b); // 0
}

但是,请注意,当传递Base对象时(实际上obj引用Base对象时),我们仍然调用Base成员函数。

答案 1 :(得分:2)

不能。 MATCH (p:Person)-[:ACTED_IN]->(m:Movie) WHERE (p)-[:DIRECTED]->(m) WITH p, m MATCH (p)-[:ACTED_IN]->(m2:Movie {title: 'Movie X'}) RETURN p, m, m2 被引用为实际的caller对象。在C ++中,无法将其转换为对Base对象的引用(因为该对象实际上不是Derived对象)。任何尝试进行转换的行为都会被未定义行为所犯规。这意味着任何事情都可能发生。具体而言,该呼叫可能似乎有效-除非您要向主要客户进行演示,否则此时它将格式化您的硬盘。 (出于夸张的目的,这有点夸张;但是未定义的行为已经将for循环运行到64,变成无限循环。)


如果我们稍微更改Derived,那么这将成为一个更有趣的问题:

main

现在我们可以安排致电int main() { Derived derived; caller(derived); } 最佳解决方案是使Derived::fun成为虚拟函数。然后就可以了:

fun

class Base { public: virtual void fun() { cout << "Base"; } }; 的调用将定向到obj.fun(如您所愿),并且代码将打印“派生”。

那太好了-绝对应该是您的第一通电话。但是,也许您无法更改Derived::fun。在这种情况下,任何功能(例如析构函数)是否是虚拟的?如果是这样,我们可以使用Base

dynamic_cast

对此的好处是,如果void caller(Base &obj) // must stay as it is { Derived& d_ref = dynamic_cast<Derived&>(obj); d_ref.fun(); } 实际上不是 一个obj对象,则dynamic_cast将抛出Derived,而您不会获得不确定的行为。

最后,也许您无法更改std::invalid_cast,没有虚函数。在这种情况下,您唯一的选择是触及Base的大手笔-但是,请注意,这是C ++程序员不但要自发自足,而且要整条脚踢。 (它不如static_cast的等离子大炮那么糟糕,但是除非您必须这样做,否则您真的不想这样使用它。)

reintepret_cast

一个人可能必须在生产代码中写这种东西,但是void caller(Base &obj) // must stay as it is { Derived& d_ref = static_cast<Derived&>(obj); d_ref.fun(); } 通常有某种“我是什么样的人”指示符,您可以在使用Base之前进行检查