我正在学习C++,我刚刚开始学习虚拟函数。

从我(在书中和网上)读到的内容来看,虚拟函数是基类中的函数,可以在派生类中重写。

但在本书的早些时候,当我学习基本继承时,我能够在派生类中重写基函数,而不使用虚函数。

那么我在这里错过了什么?我知道虚拟函数还有很多,它似乎很重要,所以我想清楚它到底是什么。我只是在网上找不到一个直截了当的答案。


当前回答

如果没有“虚拟”,您将获得“早期绑定”。在编译时,根据您调用的指针的类型来决定使用该方法的哪个实现。

使用“虚拟”,您将获得“后期绑定”。使用方法的哪种实现在运行时根据指向对象的类型来决定,即它最初的构造形式。这不一定是根据指向该对象的指针的类型来考虑的。

class Base
{
  public:
            void Method1 ()  {  std::cout << "Base::Method1" << std::endl;  }
    virtual void Method2 ()  {  std::cout << "Base::Method2" << std::endl;  }
};

class Derived : public Base
{
  public:
    void Method1 ()  {  std::cout << "Derived::Method1" << std::endl;  }
    void Method2 ()  {  std::cout << "Derived::Method2" << std::endl;  }
};

Base* basePtr = new Derived ();
  //  Note - constructed as Derived, but pointer stored as Base*

basePtr->Method1 ();  //  Prints "Base::Method1"
basePtr->Method2 ();  //  Prints "Derived::Method2"

编辑-请参阅此问题。

此外,本教程还介绍了C++中的早期和后期绑定。

其他回答

我认为您所指的是这样一个事实:一旦方法被声明为virtual,您就不需要在重写中使用“virtual”关键字。

class Base { virtual void foo(); };

class Derived : Base 
{ 
  void foo(); // this is overriding Base::foo
};

如果在Base的foo声明中不使用“virtual”,那么Derived的foo将只是隐藏它。

您需要至少1个级别的继承和一个升级来演示它。下面是一个非常简单的示例:

class Animal
{        
    public: 
      // turn the following virtual modifier on/off to see what happens
      //virtual   
      std::string Says() { return "?"; }  
};

class Dog: public Animal
{
    public: std::string Says() { return "Woof"; }
};

void test()
{
    Dog* d = new Dog();
    Animal* a = d;       // refer to Dog instance with Animal pointer

    std::cout << d->Says();   // always Woof
    std::cout << a->Says();   // Woof or ?, depends on virtual
}

如果你知道潜在的机制,这会有所帮助。C++将C程序员使用的一些编码技术形式化,用“覆盖”代替“类”-具有公共头段的结构将用于处理不同类型的对象,但具有一些公共数据或操作。通常,覆盖的基本结构(公共部分)具有指向函数表的指针,该函数表指向每个对象类型的不同例程集。C++做了同样的事情,但隐藏了机制,即C++ptr->func(…),其中func是C的虚拟(*ptr->func_table[func_num])(ptr,…),派生类之间的变化是func_table内容。[非虚拟方法ptr->func()仅转换为mangled_func(ptr,..)。]

这样做的结果是,您只需要了解基类就可以调用派生类的方法,即,如果例程了解类a,您可以向它传递派生类B指针,那么所调用的虚拟方法将是B的虚拟方法,而不是a的虚拟方法。

底线是,虚拟功能使生活更轻松。让我们使用M Perry的一些想法,并描述如果我们没有虚拟函数而只能使用成员函数指针会发生什么。在没有虚函数的正常估计中,我们有:

 class base {
 public:
 void helloWorld() { std::cout << "Hello World!"; }
  };

 class derived: public base {
 public:
 void helloWorld() { std::cout << "Greetings World!"; }
 };

 int main () {
      base hwOne;
      derived hwTwo = new derived();
      base->helloWorld(); //prints "Hello World!"
      derived->helloWorld(); //prints "Hello World!"

好的,这就是我们所知道的。现在让我们尝试使用成员函数指针:

 #include <iostream>
 using namespace std;

 class base {
 public:
 void helloWorld() { std::cout << "Hello World!"; }
 };

 class derived : public base {
 public:
 void displayHWDerived(void(derived::*hwbase)()) { (this->*hwbase)(); }
 void(derived::*hwBase)();
 void helloWorld() { std::cout << "Greetings World!"; }
 };

 int main()
 {
 base* b = new base(); //Create base object
 b->helloWorld(); // Hello World!
 void(derived::*hwBase)() = &derived::helloWorld; //create derived member 
 function pointer to base function
 derived* d = new derived(); //Create derived object. 
 d->displayHWDerived(hwBase); //Greetings World!

 char ch;
 cin >> ch;
 }

虽然我们可以用成员函数指针做一些事情,但它们不如虚拟函数灵活。在类中使用成员函数指针是很棘手的;至少在我的实践中,成员函数指针几乎总是必须在主函数中或从成员函数中调用,如上面的示例所示。

另一方面,虚拟函数虽然可能有一些函数指针开销,但确实大大简化了事情。

EDIT:还有一种方法与eddietree类似:c++虚拟函数与成员函数指针(性能比较)。

为什么我们需要C++中的虚拟方法?

快速回答:

它为我们提供了面向对象编程所需的“要素”之一。

在Bjarne Stroustrup C++编程:原理与实践中,(14.3):

虚拟函数提供了在基类中定义函数的能力,并在用户调用基类函数时在派生类中具有相同名称和类型的函数。这通常称为运行时多态性、动态调度或运行时调度,因为调用的函数是在运行时根据所使用的对象类型确定的。

如果您需要虚拟函数调用2,这是最快、更有效的实现。

为了处理虚拟调用,需要一条或多条与派生对象3相关的数据。通常的做法是添加函数表的地址。该表通常称为虚拟表或虚拟函数表,其地址通常称为虚指针。每个虚拟函数在虚拟表中都有一个槽。根据调用者的对象(派生)类型,虚拟函数依次调用相应的重写。


1.使用继承、运行时多态性和封装是面向对象编程的最常见定义。

2.您不能在运行时使用其他语言功能在备选方案中进行选择,从而使功能更快或使用更少的内存。Bjarne Stroustrup C++编程:原理与实践。(14.3.1).

3.当我们调用包含虚拟函数的基类时,可以判断哪个函数真正被调用。