2014-01-11 96 views
0

下面是在C++中的虚拟函数的也许是最简单的例子:C++虚拟方法仅适用于基类指针

#include <iostream> 

class A { 
public: 
    virtual void f() { 
     std::cout << "A"; 
    } 
}; 

class B : public A { 
public: 
    void f() { 
     std::cout << "B"; 
    } 
}; 


int main() { 
    { 
     // calls f() in derived class 
     A* a = new B(); 
     a->f(); 
    } 
    { 
     // calls f() in base class 
     A a = B(); 
     a.f(); 
    } 
} 

该程序的输出是BA。我预计它是BB,即在任何情况下调用基类。 为什么在这里使用基类指针会有所作为? 我没有在标准中找到解释。

+0

我相信这是比切片不同的问题。在这里,它似乎是对继承,多态和运行时调度的误解。他也错过了可以同时使用'基类指针'和'基类引用'的事实。 (关于这个话题可能还有另一个SO问题)。 – jww

回答

6

这叫做slicingA a = B();创建一个类型为A的副本。关于其来源B的所有信息都被遗忘了。利用多态性的唯一方法是通过引用或指针(或允许编译时多态的机制,例如模板或函数重载)。

3

多态性适用于指针,因为在指针(或引用)的情况下,您可以区分指针的类型和对象的类型。现在在下面的代码中:

A a = B(); 
a.f(); 

发生了什么叫切片。即对象B()被切割并且其基A()被分配给a。

不要忘记让析构函数变为虚拟!

+0

感谢AraK。最后,明白了。 – user3186142

0

中的A a = B()的情况下函数调用的顺序是这样的:B级

  1. 默认的构造函数被调用。
    • 类型B的临时对象在堆栈上创建。
  2. 从B到A的转换运算符被调用。
    • 类型A的临时对象在堆栈上创建。
  3. 类A的拷贝构造函数以前一个对象的地址作为参数被调用。
    • 类型A的非临时对象在堆栈上创建。
    • 该对象将保留在堆栈中,直到函数返回。
    • 它是A类型的一个对象,因此您可以打印出“A”而不是“B”。

总结这一点,这里是上面一行的“完整版”:A(B()->operator A())