[英]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
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.