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

时间:2011-01-01 14:19:04

标签: c++ function inheritance polymorphism override

#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类对象?

7 个答案:

答案 0 :(得分:13)

A a = *(A *)new B();
a.func();

以下是此代码中一步一步发生的事情:

  • new B():在免费商店中分配类型B的新对象,从而生成其地址
  • (A*):对象的地址被强制转换为A*,因此我们有一个类型为A*的指针实际指向类型B的对象,该对象是有效的。一切OK。
  • A a:问题开始了。在堆栈上创建一个新的本地对象,并使用复制构造函数A::A(const A&)构造,第一个参数是之前创建的对象。
  • 在此语句之后,指向B类原始对象的指针将丢失,从而导致内存泄漏,因为它是使用new在免费存储上分配的。
  • a.func() - 在A类的(本地)对象上调用该方法。

如果您将代码更改为:

A& a = *( A*) new B();
a.func();

然后只构造一个对象,其指针将转换为A*类型的指针,然后取消引用,并使用此地址初始化新的引用。然后,虚拟函数的调用将动态解析为B::func()


但请记住,您仍然需要释放该对象,因为它已分配new

delete &a;

顺便说一句,只有当A有一个虚拟析构函数时才会正确,这需要B ::〜B()(幸运的是这里是空的,但在一般情况下它不需要)也将被称为。如果A没有虚拟析构函数,那么您需要通过以下方式释放它:

delete (B*)&a;

如果您想使用指针,那么它与引用相同。代码:

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.

答案 1 :(得分:5)

您遇到的问题是经典object slicing

A a = *(A *)new B();

使a成为A的引用或指针,虚拟分派将按预期工作。有关详细说明,请参阅this other question


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

答案 2 :(得分:5)

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

如果你A *a = new B();,那么你将得到预期的行为。

答案 3 :(得分:1)

这可能就是这个伎俩。

A &a = *(A *)new B();
a.func();

或者

A *a = new B();
a->func();

答案 4 :(得分:1)

虚拟调度仅适用于指针或引用类型:

#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 = new B();
  a->func();
}

答案 5 :(得分:0)

问题是使用A a = *(A *)new B()对B进行A的推理和转换;

您只需删除*(A *)将其更改为(A * a = new B();)即可修复它,但我会更进一步,因为您的变量名称不适合B的实例化。

应该是

B *b = new B(); 
b->func(); 

答案 6 :(得分:0)

因为您在将动态分配的对象复制到a类型的对象A时执行了切片(这也给了您内存泄漏)。

a应该是引用(A&),或者只是保留指针。