运算符重载和继承
我得到了以下代码:
class FibHeapNode
{
//...
// These all have trivial implementation
virtual void operator =(FibHeapNode& RHS);
virtual int operator ==(FibHeapNode& RHS);
virtual int operator <(FibHeapNode& RHS);
};
class Event : public FibHeapNode
{
// These have nontrivial implementation
virtual void operator=(FibHeapNode& RHS);
virtual int operator==(FibHeapNode& RHS);
virtual int operator<(FibHeapNode& RHS);
};
class FibHeap
{
//...
int DecreaseKey(FibHeapNode *theNode, FibHeapNode& NewKey)
{
FibHeapNode *theParent;
// Some code
if (theParent != NULL && *theNode < *theParent)
{
//...
}
//...
return 1;
}
};
FibHeap 的大部分实现都是相似的:FibHeapNode 指针被取消引用,然后进行比较。
为什么这段代码有效? (还是有问题?)我认为这里的 virtual 不会有任何效果:因为 *theNode 和 *theParent 不是指针或引用类型,所以不会发生动态调度并且 FibHeapNode::operator< ;无论事件中写了什么,都会被调用。
I was given the following code:
class FibHeapNode
{
//...
// These all have trivial implementation
virtual void operator =(FibHeapNode& RHS);
virtual int operator ==(FibHeapNode& RHS);
virtual int operator <(FibHeapNode& RHS);
};
class Event : public FibHeapNode
{
// These have nontrivial implementation
virtual void operator=(FibHeapNode& RHS);
virtual int operator==(FibHeapNode& RHS);
virtual int operator<(FibHeapNode& RHS);
};
class FibHeap
{
//...
int DecreaseKey(FibHeapNode *theNode, FibHeapNode& NewKey)
{
FibHeapNode *theParent;
// Some code
if (theParent != NULL && *theNode < *theParent)
{
//...
}
//...
return 1;
}
};
Much of FibHeap's implementation is similar: FibHeapNode pointers are dereferenced and then compared.
Why does this code work? (or is it buggy?) I would think that the virtual
s here would have no effect: since *theNode and *theParent aren't pointer or reference types, no dynamic dispatch occurs and FibHeapNode::operator< gets called no matter what's written in Event.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您一定对动态调度有点困惑。
人们常说“动态调度仅在通过指针或引用进行调用时才会发生”。从形式上来说,这种说法完全是虚假和误导性的。
当您调用虚拟函数时,C++ 中的动态分派总是发生,只有一个例外:当您使用目标函数的完全限定名称时,动态分派被禁用。例如:
在上面的代码中,即使
some_function
是一个虚函数,调用也会被静态调度。从语言的角度来看,没有其他方法可以避免动态分派,即在所有其他情况下,所有对虚拟函数的调用都是动态分派的>。你使用什么:指针、引用、直接对象 - 没关系,调度仍然是动态的。从哪里调用函数:从构造函数/析构函数或从其他地方调用函数 - 并不重要,调度仍然是动态的。我再说一遍:从 C++ 语言本身的角度来看,事情就是这样的。这就是“抽象 C++ 机器”的工作原理。但实际上,在许多情况下,动态分派可以替换为静态分派,因为编译器在编译时提前知道对象的动态类型,因此知道分派的目标。在这种情况下,直接调用目标函数更有意义,而不是通过成本更高的动态调度机制。然而,这只不过是一种优化。不幸的是,有些人错误地认为对语言强制行为的优化,提出了诸如“动态调度仅在通过指针或引用进行调用时才会发生”之类的毫无意义的语句。
在您的具体情况下,调度是动态的。由于在您的情况下,编译器不知道所涉及对象的动态类型,因此它无法将其优化为静态分派,这就是您的代码“按预期工作”的原因。
PS 预测关于我上面所说的内容可能存在的问题:构造函数/析构函数调用的动态调度受到对象当前动态类型的限制,这就是为什么在简单的情况下编译器可以(并且容易)优化它们进入静态调度。这就是另一个流行的都市传说的原因,该传说指出来自构造函数/析构函数的虚拟调用是静态解析的。实际上,在一般情况下,它们是动态解析的,正如它们应该的那样(再次观察对象的当前动态类型)。
You must be a bit confused about dynamic dispatch.
It is often said that "dynamic dispatch occurs only when you make a call through a pointer or through a reference". Formally speaking, this statement is totally bogus and misleading.
Dynamic dispatch in C++ happens always when you call a virtual function, with one and only one exception: dynamic dispatch is disabled when you use a fully qualified-name of the target function. For example:
In the above code the call will be dispatched statically, even if
some_function
is a virtual function. From the language point of view, there's no other ways to avoid the dynamic dispatch, i.e. in all other cases all calls to virtual functions are dispatched dynamically. What you use: a pointer, a reference, an immediate object - does not matter, the dispatch is still dynamic. Where you are calling the function from: from constructor/destructor or from somewhere else - does not matter, the dispatch is still dynamic. And I repeat: this is how things are from the point of view of the C++ language itself. This is how an "abstract C++ machine" works.What happens in practice though, is that in many cases the dynamic dispatch can be replaced with static dispatch, because the compiler knows the dynamic type of the object in advance, at the compile-time and, consequently, knows the target of the dispatch. In such cases it makes more sense to call the target function directly, instead of going through the costlier dynamic dispatch mechanism. Yet, this is nothing else than just an optimization. Some people, unfortunately, mistake that optimization for language-mandated behavior, coming up with such meaningless statements as "dynamic dispatch occurs only when you make a call through a pointer or through a reference".
In your specific case the dispatch is dynamic. Since in your case the compiler does not know the dynamic types of the objects involved, it cannot optimize it into a static dispatch, which is why your code "works as intended".
P.S. Anticipating the possible questions about something I said above: Dynamic dispatch for calls made from constructors/destructors is limited by the current dynamic type of the object, which is why in straightforward cases it is possible (and easy) for the compiler to optimize them into a static dispatch. This is the reason for another popular urban legend that states that virtual calls from constructors/destructors are resolved statically. In reality, in general case they are resolved dynamically, as they should (again, observing the current dynamic type of the object).
*theNode 和 *theParent 是引用类型。事实上 FibHeapNode& ref 与 *theNode 非常等效。
*theNode and *theParent are reference types. Infact FibHeapNode& ref is very equivalent to *theNode.