简体   繁体   English

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

[英]Calling member of derived class from Base reference object

I found many similar questions on SO, but their solution is either not working or it's slightly different (often using pointers instead, I do not want to change the caller() signature!).我在 SO 上发现了许多类似的问题,但他们的解决方案要么不起作用,要么略有不同(通常使用指针代替,我不想更改 caller() 签名!)。 If this has already been asked (and answered) please let me know and I'll delete this question.如果已经有人问过(并回答过),请告诉我,我会删除这个问题。

I found something called the visitor pattern but I'm curious if there is a simpler solution.我发现了一种叫做访问者模式的东西,但我很好奇是否有更简单的解决方案。 I try do do it without using virtual since my course did not cover that yet.我尝试在不使用virtual情况下进行,因为我的课程还没有涵盖。 Or do I need it here?或者我这里需要它?

I have the following code:我有以下代码:

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);

}

What would be the easiest way to have caller() call Derived::fun() instead of Base::fun() when passing a Base & to the function caller()?在将Base &传递给函数 caller() 时,让 caller() 调用 Derived::fun() 而不是 Base::fun() 的最简单方法是什么?

If there's a simple answer pointing me in the right direction would be highly appreciated, I've been stuck on this for a while now:)如果有一个简单的答案指向我正确的方向,我将不胜感激,我已经坚持了一段时间了:)

I know that this is not something you would do in production code, this is about learning C++我知道这不是你会在生产代码中做的事情,这是关于学习 C++

EDIT: I changed the caller function argument to type Derived instead of Base编辑:我将调用函数参数更改为类型Derived而不是Base

It is not possible to call a Derived member function on a Base class object.无法在Base类对象上调用Derived成员函数。

Every Derived is a Base , but a Base is not a Derived .每个Derived都是一个Base ,但Base不是Derived In other words, Derived may have a whole bunch of information that Base lacks.换句话说, Derived可能拥有Base缺乏的一大堆信息。 You may call a Base member function on a Derived class object, but not the other way .您可以在Derived类对象上调用Base成员函数,但不能以其他方式调用

In other words...换句话说...

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 :(
}

However, you can have a Base reference that references a Derived object:但是,您可以拥有一个引用Derived对象的Base引用:

Base &b_ref = d;

And since this is actually a Derived object, you can have it call Derived member functions (that were declared virtual in Base ) via polymorphism:由于这实际上是一个Derived对象,您可以让它通过多态调用Derived成员函数(在Base中声明为virtual ):

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
}

However, notice that when passed a Base object (when obj is actually referencing a Base object), we still call the Base member function.但是,请注意,当传递一个Base对象时(当obj实际上引用一个Base对象时),我们仍然调用Base成员函数。

You can't.你不能。 caller is given a reference to an actual Base object. caller获得了对实际Base对象的引用。 There is no way in C++ to convert this to a reference to a Derived object (because the object actually is not a Derived object).在 C++ 中没有办法将其转换为对Derived对象的引用(因为该对象实际上不是Derived对象)。 Any attempt to do the conversion will fall foul of Undefined Behaviour .任何进行转换的尝试都会违反Undefined Behavior This means that anything may happen.这意味着任何事情都可能发生。 Specifically, the call may appear to work - except when you come to do the demonstration to a major customer at which point it formats your hard disk instead.具体来说,该呼叫可能看起来有效 - 除非您来向主要客户进行演示,此时它会格式化您的硬盘。 (This is a slight exaggeration for rhetorical purposes; but undefined behaviour has turned a for loop running up to 64 into an infinite loop). (出于修辞目的,这有点夸大其词;但未定义的行为已将最多运行 64 次的 for 循环变成了无限循环)。


If we change main slightly, then this becomes a much more interesting question:如果我们稍微改变main ,那么这将成为一个更有趣的问题:

int main()
{
    Derived derived;
    caller(derived);

}

Now we can arrange to call Derived::fun .现在我们可以安排调用Derived::fun The best solution is to make fun a virtual function.最好的解决办法是让fun成为一个虚函数。 Then it just works:然后它就起作用了:

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

The call to obj.fun will be directed to Derived::fun (like you wanted), and the code will print "derived".obj.fun的调用将被定向到Derived::fun (如您所愿),并且代码将打印“派生”。

That's great - and definitely should be your first port of call.太好了 - 绝对应该是您的第一个停靠港。 However maybe you can't change Base .但是,也许您无法更改Base In that case, are any of the functions (like the destructor) virtual?在这种情况下,是否有任何函数(如析构函数)是虚拟的? If so, we can use dynamic_cast :如果是这样,我们可以使用dynamic_cast

void caller(Base &obj)   // must stay as it is
{
    Derived& d_ref = dynamic_cast<Derived&>(obj);
    d_ref.fun();
}

The nice thing about this, is that if obj is not actually a Derived object, the dynamic_cast will throw std::invalid_cast , and you don't get undefined behaviour.这样做的好处是,如果obj实际上不是Derived对象,则 dynamic_cast 将抛出std::invalid_cast ,并且您不会得到未定义的行为。

Finally, maybe you can't change Base , and there are no virtual functions.最后,也许你不能改变Base而且没有虚函数。 In that case, your only option is to reach for the blunderbuss of static_cast - however, be warned, this is how C++ programmers not merely shoot themselves in the foot, but blow their whole leg off.在这种情况下,你唯一的选择就是去寻找static_cast ——但是,请注意,这就是 C++ 程序员如何不仅用脚开枪,而且把整条腿都炸掉。 (It's not quite as bad as the plasma cannon of reintepret_cast , but you really don't want to use it like this unless you have to.) (它不像reintepret_cast的等离子炮那么糟糕,但除非必须,否则您真的不想这样使用它。)

void caller(Base &obj)   // must stay as it is
{
    Derived& d_ref = static_cast<Derived&>(obj);
    d_ref.fun();
}

One can have to write this sort of thing in production code, but often Base has some sort of "what kind of thing am I" indicator, and you can check that before using the static_cast .人们可能不得不在生产代码中编写这种东西,但Base通常有某种“我是什么东西”的指示器,您可以在使用static_cast之前检查它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM