C++中的覆盖函数不起作用

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()调用类中的函数?Aa

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是之前创建的对象.
  • 在此语句之后,指向类型B的原始对象的指针将丢失,从而导致内存泄漏,因为它是在免费存储上分配的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)


ice*_*ime 5

您遇到的问题是经典对象切片:

A a = *(A *)new B();
Run Code Online (Sandbox Code Playgroud)

制作a引用或指针A,虚拟分派将按预期工作.有关更多解释,请参阅此其他问题.


您评论了另一个答案,"编译器至少应该发出警告或者什么".这就是为什么将基类设为非可复制的抽象被认为是一种好的做法:您的初始代码不会首先编译.


Oli*_*rth 5

现在您已修改了代码段,问题很明显.多态性(即虚函数)仅通过指针和引用调用.你没有这些. A a = XXX它不包含类型的对象B,它包含一个类型的对象A.你B通过执行指针转换和取消引用来"切掉" 对象的强度.

如果这样做A *a = new B();,那么您将获得预期的行为.