我在虚拟函数中遇到了一个问题:以下是一些代码作为示例:
class A
{
public : virtual void print(void)
{
cout<< "A::print()"<<endl;
}
};
class B : public A
{
public : virtual void print(void)
{
cout<<"B::print()"<<endl;
}
};
class C : public A
{
public : void print(void)
{
cout<<"C::print()"<<endl;
}
};
int main(void)
{
A a,*pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
pa->print();
pb->print();
pc->print();
a=b;
a.print();
return 0;
}
结果:A: :print()B: :print()C: :print()A: :print()
我知道这是一个多态性,也知道有一个叫做虚拟函数表的表,但我不知道它是如何实现的,还有
a=b;
a.print();
结果是:A::print()而不是B::print(,为什么它没有多态性。非常感谢。
对象a
仍然是类型A
。该赋值只复制来自b
的数据,并不使a
成为B
对象。
这称为对象切片。
a=b;
a.print();
它将打印A::print()
,因为a=b
会导致对象切片,这意味着a
只获得b
的子对象。阅读:
- 什么是对象切片
请注意,运行时多态性只能通过指针和引用类型实现。在上面的代码中,a
既不是指针,也不是reference类型:
A * ptr = &b; //syntax : * on LHS, & on RHS
A & ref = b; //syntax : & on LHS, that is it!
ptr->print(); //will call B::print() (which you've already seen)
ref.print(); //will call B::print() (which you've not seen yet)
因为a
不是指针。它是A
的实例,分配a=b;
将b
的实例复制到a
。但是函数调用是在A
的一个实例上进行的。
执行a = b;
时,会对b
对象进行切片,即只复制其A
部分。多态性只通过指针和引用起作用。搜索"对象切片"以了解该主题。
若要了解有关虚拟方法表的更多信息,请访问wiki。但在一般情况下,该表保留信息方法的地址。因此,表中的类A将有一条记录,说明方法print在地址X中。当您执行pa=&b类b只需将表替换为其一,方法print的地址将指向地址Y。
但当您执行a=b时,您将复制对象。在这种情况下,多态性不起作用。
在类型为A
的对象上调用任何成员函数后,仍然有A
对象(除了显式析构函数调用,它什么都不留下)。
a = b;
类实例的赋值只是对名为"operator=
"的特定成员函数的调用。这里的"operator=
"没有什么特别之处,只是它的名字是标准的。您可以使用另一个名称进行分配:
a = b;
// you could as well write:
a.assign(b);
// (if such member was defined)
正如您可以编写add(a,b)
而不是a+b
一样,但a+b
更可读。
对函数的显式调用永远不会改变调用它的变量的类型:
A a;
foo(a);
// a is still a A
a.bar();
// a is still a A
声明的a
类型是A
,不能更改为其他类型:这是a
的不变量。
指针也是如此:指向A
的类型指针的变量将始终具有指向A
的类型指针:
void foo (A*&);
A *bar();
A a;
A *p = &a;
foo (p); // might change p
// a still has type: pointer to A
p = bar();
// a still has type: pointer to A
但是p
可能指向B
类型的对象,因此在运行时,*p
的动态类型将是B
;但CCD_ 36的动态类型始终为CCD_ 37。