mnn*_*mnn 6 c++ polymorphism inheritance overriding function
#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();
}
Run Code Online (Sandbox Code Playgroud)
问题很简单:为什么即使包含B类对象,也要a->func()
调用类中的函数?A
a
Kos*_*Kos 13
A a = *(A *)new B();
a.func();
Run Code Online (Sandbox Code Playgroud)
以下是此代码中一步一步发生的情况:
new B()
:在免费商店中分配类型B的新对象,从而生成其地址(A*)
:对象的地址被强制转换为A*
,所以我们有一个A*
实际指向类型为B的对象的类型指针,它是有效的.一切都好.A a
:这里的问题开始了.在堆栈上创建A类型的新本地对象,并使用复制构造函数构造A::A(const A&)
,第一个paremeter是之前创建的对象.new
.a.func()
- 在A类的(本地)对象上调用该方法.如果您将代码更改为:
A& a = *( A*) new B();
a.func();
Run Code Online (Sandbox Code Playgroud)
然后只构造一个对象,将其指针转换为类型的指针A*
,然后解除引用,并使用该地址初始化新引用.然后将动态解析虚拟函数的调用B::func()
.
但请记住,您仍需要释放该对象,因为它已分配new
:
delete &a;
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果A有一个虚拟的析构函数,那将是正确的,这需要B ::〜B()(幸运的是这里是空的,但在一般情况下它不需要)也将是调用.如果A没有虚拟析构函数,那么您需要通过以下方式释放它:
delete (B*)&a;
Run Code Online (Sandbox Code Playgroud)
如果您想使用指针,那么与引用相同.码:
A* a = new B(); // actually you don't need an explicit cast here.
a->func();
delete (B*)a; // or just delete a; if A has a virtual destructor.
Run Code Online (Sandbox Code Playgroud)
您遇到的问题是经典对象切片:
A a = *(A *)new B();
Run Code Online (Sandbox Code Playgroud)
制作a
引用或指针A
,虚拟分派将按预期工作.有关更多解释,请参阅此其他问题.
您评论了另一个答案,"编译器至少应该发出警告或者什么".这就是为什么将基类设为非可复制的抽象被认为是一种好的做法:您的初始代码不会首先编译.
现在您已修改了代码段,问题很明显.多态性(即虚函数)仅通过指针和引用调用.你没有这些. A a = XXX
它不包含类型的对象B
,它包含一个类型的对象A
.你B
通过执行指针转换和取消引用来"切掉" 对象的强度.
如果这样做A *a = new B();
,那么您将获得预期的行为.