我的代码:
#include <iostream>
using namespace std;
class A
{
public:
virtual void print(void) { cout << "I am base class" << endl; }
};
class B : public A
{
public:
void print(void) { cout << "I am class B" << endl; }
};
void mainprint(A *a)
{
(*a).print();
}
int main()
{
A a;
B b;
B *bp;
A *ap;
ap = &b;
a.print();
b.print();
(*ap).print();
bp = new B();
mainprint((A *)bp);
delete bp;
return 0;
}
输出:
I am base class
I am class B
I am class B
I am class B
我已经将指针(bp)转换为函数调用中的A类,但它仍然调用派生类print!
有人可以为我阐明这一点。
答案 0 :(得分:6)
我已经将指针(bp)转换为函数调用中的A类,但它仍然调用基类打印!!!
我认为你的意思是“调用派生类打印”,因为那就是发生的事情。
这是虚拟功能的重点;无论用于调用函数的引用或指针的类型(即“静态类型”)如何,都选择与对象的实际类型相关联的最终超控(即“动态类型”)。因此选择了B::print
,因为bp
仍然指向B
的实例。
如果您想强制拨打A::print
,可以执行以下操作:
pb->A::print()
或者,如果您根本不想要多态行为,请删除virtual
规范。
答案 1 :(得分:3)
您已A::print()
成为virtual function。
答案 2 :(得分:2)
它是虚函数调度(即运行时多态),按预期工作。
禁用虚函数调度的一种方法是明确使用类名限定函数名,如下所示:
void mainprint(A *a)
{
(*a).A::print();
}
答案 3 :(得分:1)
你在期待什么?这就是多态性的工作原理。
此行为是预期且正确的。
此外,
(*a).print();
不是强制转换,而是取消引用。
答案 4 :(得分:1)
我无法解决问题所在......第一个是print
类型A
的实际对象上的print
,其他所有对象都是B
的调用B
对象(直接或通过指针),因此它会调用print
的{{1}}。
请记住,在函数调用中,您没有将B
类型的对象强制转换为A
类型的对象(其中导致切片),但你只是向它投射一个指针 - 对象本身保持不变,并且由于虚拟调度,即使对象的静态类型是A *
,也会调用正确的虚函数版本
这就是virtual
功能的工作方式。
答案 5 :(得分:0)
它没有调用基类打印,它在最后一次调用时调用派生类print(我是B类),这是正确的行为,因为指针和引用与多态性一起使用。
如果您切割对象然后调用print,它确实会打印A版本。你不想这样做,幸运的是你没有。
这将是切片:
B b;
A a(b);
A a2;
a2 = b;
A& a3 = b;
b.print();
a.print();
a2.print();
a3.print();
你应该得到:
我是B班 我是基础班 我是基础班 我是B班
因为a
和a2
都是类型a的对象,即使您已将它们分配给b。
不建议切片,但这是一个常见的错误。
a3是对A类或其衍生物之一的对象的引用,但实际上是B类型的对象b并且保留了多态行为。