C++ 中的重写函数不起作用
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A a = *(A *)new B();
a.func();
}
问题很简单:为什么 a->func()
调用类 A
中的函数,即使 a
包含类 B 的对象?
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A a = *(A *)new B();
a.func();
}
The question is simple: why a->func()
calls function in class A
even though a
contains object of class B?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
以下是此代码中逐步发生的情况:
new B()
:在空闲存储上分配 B 类型的新对象,产生其地址(A*)
>:对象的地址被强制转换为A*
,因此我们有一个A*
类型的指针实际上指向B类型的对象,这是有效的。一切都好。A a
:问题从这里开始。在堆栈上创建一个新的 A 类型本地对象,并使用复制构造函数A::A(const A&)
构造,第一个参数是创建的对象前。a.func()
- 该方法在类 A 的(本地)对象上调用。如果将代码更改为:
则只会构造一个对象,其指针将转换为输入
A*
,然后取消引用,并且将使用此地址初始化新的引用。虚函数的调用将被动态解析为B::func()
。但请记住,您仍然需要释放该对象,因为它是通过 new 分配的:
顺便说一句,只有当 A 有一个虚拟析构函数时,这才是正确的,而 B 需要这样: :~B() (幸运的是这里是空的,但在一般情况下不需要)也将被调用。如果 A 没有虚拟析构函数,那么您需要通过以下方式释放它:
如果您想使用指针,那么这与引用相同。代码:
Here's what happens in this code, step by step:
new B()
: a new object of type B is allocated on the free store, resulting in its address(A*)
: the address of the object is cast toA*
, so we have a pointer of typeA*
actually pointing to an object of type B, which is valid. All OK.A a
: here the problems start. A new local object of type A is created on the stack and constructed using the copy constructorA::A(const A&)
, with the first paremeter being the object created before.new
.a.func()
- the method is called on the (local) object of class A.If you change the code to:
then only one object will be constructed, its pointer will be converted to pointer of type
A*
, then dereferenced and a new reference will be initialized with this address. The call of the virtual function will then be dynamically resolved toB::func()
.But remember, that you'd still need to free the object since it was allocated with
new
:Which, by the way, will only be correct if A has a virtual destructor, which is required that B::~B() (which luckily is empty here, but it doesn't need to in the general case) will also be called. If A doesn't have a virtual destructor, then you'd need to free it by:
If you would want to use a pointer, then that's the same as with the reference. Code:
现在您已经修改了代码片段,问题就清楚了。多态性(即虚函数)只能通过指针和引用来调用。这些你都没有。
A a = XXX
不包含B
类型的对象,它包含A
类型的对象。通过执行指针转换和取消引用,您已经“切掉”了对象的 B 性。如果您执行
A *a = new B();
,那么您将获得预期的行为。Now that you've modified your code snippet, the problem is clear. Polymorphism (i.e. virtual functions) are only invoked via pointers and references. You have neither of these.
A a = XXX
does not contain an object of typeB
, it contains an object of typeA
. You've "sliced away" theB
-ness of the object by doing that pointer cast and dereference.If you do
A *a = new B();
, then you will get the expected behaviour.您遇到的问题是经典的 对象切片 :
使
a
要么指向A
的引用或指针,虚拟调度将按您的预期工作。有关更多说明,请参阅这个其他问题。您评论了另一个答案“编译器至少应该发出警告或什么”。这就是为什么将基类设置为抽象或不可复制被认为是一个好习惯:您的初始代码一开始就不会编译。
The problem you encounter is classic object slicing :
Make
a
either a reference or pointer toA
, and virtual dispatch will work as you expect. See this other question for more explanations.You commented on another answer that "Compiler should at least give warning or what". This is why is it considered a good practice to make base classes either abstract of non copyable : your initial code wouldn't have compiled in the first place.
这也许可以做到这一点。
或者
This might do that trick.
Or
虚拟调度仅适用于指针或引用类型:
Virtual dispatch works only with pointer or reference types:
问题是使用 A a = *(A *)new B(); 将 B 遵从并强制转换为 A;
您可以通过删除 *(A *) 将其更改为 (A *a = new B(); ) 来修复它,但我会更进一步,因为您的变量名不适合 B 的实例化。
它应该是
The problem is the deference and casting of B to A with the A a = *(A *)new B();
You can fix it with just removing the *(A *) changing it to (A *a = new B(); ) but I would take it a step further since your variable name is not good for instantiation of B.
It should be
因为当您将动态分配的对象复制到
A
类型的对象a
时,您执行了切片(这也会导致内存泄漏)。a
应该是一个引用 (A&
),或者只保留指针。Because you performed slicing when you copied the dynamically allocated object into object
a
of typeA
(which also gave you a memory leak).a
should be a reference (A&
) instead, or just keep the pointer.