C++ 中的重写函数不起作用

发布于 2024-10-09 22:17:48 字数 370 浏览 1 评论 0原文

#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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

灰色世界里的红玫瑰 2024-10-16 22:17:48
A a = *(A *)new B();
a.func();

以下是此代码中逐步发生的情况:

  • new B():在空闲存储上分配 B 类型的新对象,产生其地址
  • (A*) >:对象的地址被强制转换为A*,因此我们有一个A*类型的指针实际上指向B类型的对象,这是有效的。一切都好。
  • A 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.
A a = *(A *)new B();
a.func();

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 to A*, so we have a pointer of type A* 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 constructor A::A(const A&), with the first paremeter being the object created before.
  • The pointer to the original object of type B is lost after this statement, resulting in a memory leak, since it was allocated on the free store with new.
  • a.func() - the method is called on the (local) object of class A.

If you change the code to:

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

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 to B::func().


But remember, that you'd still need to free the object since it was allocated with new:

delete &a;

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:

delete (B*)&a;

If you would want to use a pointer, then that's the same as with the reference. Code:

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.
风吹过旳痕迹 2024-10-16 22:17:48

现在您已经修改了代码片段,问题就清楚了。多态性(即虚函数)只能通过指针和引用来调用。这些你都没有。 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 type B, it contains an object of type A. You've "sliced away" the B-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.

夏天碎花小短裙 2024-10-16 22:17:48

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

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

使 a 要么指向 A 的引用或指针,虚拟调度将按您的预期工作。有关更多说明,请参阅这个其他问题


您评论了另一个答案“编译器至少应该发出警告或什么”。这就是为什么将基类设置为抽象或不可复制被认为是一个好习惯:您的初始代码一开始就不会编译。

The problem you encounter is classic object slicing :

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

Make a either a reference or pointer to A, 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.

追我者格杀勿论 2024-10-16 22:17:48

这也许可以做到这一点。

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

或者

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

This might do that trick.

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

Or

A *a = new B();
a->func();
墨落成白 2024-10-16 22:17:48

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

#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();
}

Virtual dispatch works only with pointer or reference types:

#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();
}
一曲琵琶半遮面シ 2024-10-16 22:17:48

问题是使用 A a = *(A *)new B(); 将 B 遵从并强制转换为 A;

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

它应该是

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

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

B *b = new B(); 
b->func(); 
紫南 2024-10-16 22:17:48

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

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

Because you performed slicing when you copied the dynamically allocated object into object a of type A (which also gave you a memory leak).

a should be a reference (A&) instead, or just keep the pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文