如果一个函数被定义为虚函数,它究竟意味着什么?它与纯虚函数是相同的吗?


当前回答

virtual关键字是如何工作的?

假设人是一个基类,那么印度人就是由人演变而来的。

Class Man
{
 public: 
   virtual void do_work()
   {}
}

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}

将do_work()声明为virtual仅仅意味着:调用哪个do_work()只在运行时决定。

假设我这样做了,

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

如果不使用virtual,则由编译器静态确定或静态绑定相同的对象,具体取决于调用的对象。因此,如果Man的对象调用do_work(),则Man的do_work()将被调用,即使它指向一个印度对象

我相信投票最多的答案是误导性的——任何方法,无论是否虚,都可以在派生类中有一个重写的实现。具体到c++,正确的区别是相关函数的运行时(使用virtual时)绑定和编译时(不使用virtual但重写方法且基指针指向派生对象时)绑定。

似乎还有另一个误导性的评论说,

“贾斯汀,‘纯虚拟’只是一个术语(不是关键字,请看我的回答 下面)用来表示“此函数不能由基实现。 类。”

这是错误的! 纯虚函数也可以有一个体并且可以实现!事实上,抽象类的纯虚函数可以被静态调用!两位非常优秀的作者是Bjarne Stroustrup和Stan Lippman....因为语言是他们写的。

其他回答

Virtual functions must have a definition in base class and also in derived class but not necessary, for example ToString() or toString() function is a Virtual so you can provide your own implementation by overriding it in user-defined class(es). Virtual functions are declared and defined in normal class. Pure virtual function must be declared ending with "= 0" and it can only be declared in abstract class. An abstract class having a pure virtual function(s) cannot have a definition(s) of that pure virtual functions, so it implies that implementation must be provided in class(es) that derived from that abstract class.

虚函数或虚方法是一种函数或方法,其行为可以在继承类中被具有相同签名的函数覆盖

这并不是对虚函数的一个很好的解释。因为,即使成员不是虚成员,继承类也可以重写它。你可以自己去看看。

当函数以基类作为参数时,这种差异就会显现出来。当您将继承类作为输入时,该函数将使用被覆盖函数的基类实现。但是,如果该函数是虚函数,则使用派生类中实现的函数。

Simula、c++和c#在默认情况下使用静态方法绑定,程序员可以通过将特定方法标记为虚拟来指定它们应该使用动态绑定。 动态方法绑定是面向对象编程的核心。

面向对象编程需要三个基本概念:封装、继承和动态方法绑定。

的实现细节 抽象要隐藏在背后 简单的接口。 继承允许将新的抽象定义为 某些的扩展或改进 现有的抽象,获得一些 或者说它的所有特征 自动。 动态方法绑定允许新抽象显示其新抽象 行为,即使在上下文中使用 这需要旧的抽象。

virtual关键字赋予c++支持多态的能力。当你有一个指向某个类的对象的指针时,例如:

class Animal
{
  public:
    virtual int GetNumberOfLegs() = 0;
};

class Duck : public Animal
{
  public:
     int GetNumberOfLegs() { return 2; }
};

class Horse : public Animal
{
  public:
     int GetNumberOfLegs() { return 4; }
};

void SomeFunction(Animal * pAnimal)
{
  cout << pAnimal->GetNumberOfLegs();
}

在这个(愚蠢的)示例中,GetNumberOfLegs()函数根据所调用对象的类返回适当的数字。

现在,考虑函数“SomeFunction”。它不关心传递给它的是什么类型的animal对象,只要它是从animal派生的。编译器会自动地将任何Animal派生类强制转换为Animal,因为它是一个基类。

如果我们这样做:

Duck d;
SomeFunction(&d);

它会输出'2'。如果我们这样做:

Horse h;
SomeFunction(&h);

它会输出'4'。我们不能这样做:

Animal a;
SomeFunction(&a);

因为它不会编译,因为GetNumberOfLegs()虚函数是纯的,这意味着它必须通过派生类(子类)来实现。

纯虚函数主要用于定义:

A)抽象类

这些是基类,你必须从它们中派生出来,然后实现纯虚函数。

b)接口

这些是“空”类,其中所有的函数都是纯虚的,因此你必须派生然后实现所有的函数。

“Virtual”意味着该方法可以在子类中重写,但在基类中有一个可直接调用的实现。“纯虚拟”意味着它是一个没有直接可调用实现的虚拟方法。这样的方法必须在继承层次结构中至少重写一次——如果一个类有任何未实现的虚方法,该类的对象就不能被构造,编译就会失败。

@夸克指出,纯虚拟方法可以有一个实现,但由于纯虚拟方法必须被覆盖,所以不能直接调用默认的实现。下面是一个带默认值的纯虚方法的例子:

#include <cstdio>

class A {
public:
    virtual void Hello() = 0;
};

void A::Hello() {
    printf("A::Hello\n");
}

class B : public A {
public:
    void Hello() {
        printf("B::Hello\n");
        A::Hello();
    }
};

int main() {
    /* Prints:
           B::Hello
           A::Hello
    */
    B b;
    b.Hello();
    return 0;
}

根据注释,编译是否失败是特定于编译器的。至少在GCC 4.3.3中,它不会编译:

class A {
public:
    virtual void Hello() = 0;
};

int main()
{
    A a;
    return 0;
}

输出:

$ g++ -c virt.cpp 
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note:   because the following virtual functions are pure within ‘A’:
virt.cpp:3: note:   virtual void A::Hello()