C++ 虚拟/纯虚拟

C++ 虚拟/纯虚拟,c++,virtual,C++,Virtual,如果一个函数被定义为virtual并且与纯virtual相同,那么它到底意味着什么?virtual表示该方法可以在子类中重写,但在基类中有一个可直接调用的实现。“纯虚拟”意味着它是一个虚拟方法,没有可直接调用的实现。这种方法必须在继承层次结构中至少重写一次——如果一个类有任何未实现的虚拟方法,则无法构造该类的对象,编译将失败 @quark指出,纯虚拟方法可以有一个实现,但由于必须重写纯虚拟方法,因此不能直接调用默认实现。以下是具有默认值的纯虚拟方法的示例: #include <cstdio

如果一个函数被定义为virtual并且与纯virtual相同,那么它到底意味着什么?

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

@quark指出,纯虚拟方法可以有一个实现,但由于必须重写纯虚拟方法,因此不能直接调用默认实现。以下是具有默认值的纯虚拟方法的示例:

#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;
}
输出:

$ 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()

在面向对象编程中,在C++等语言和对象Pascal中,虚拟函数或虚拟方法是一种可继承和可重写的函数或方法,用于动态调度。这个概念是面向对象编程(OOP)的(运行时)多态部分的一个重要部分。简而言之,虚拟函数定义了要执行的目标函数,但在编译时可能不知道目标函数

与非虚拟函数不同,当重写虚拟函数时,最派生的版本将用于类层次结构的所有级别,而不仅仅是创建它的级别。因此,如果基类的一个方法调用虚方法,则将使用派生类中定义的版本,而不是基类中定义的版本

这与非虚拟函数相反,非虚拟函数仍然可以在派生类中重写,但“新”版本将仅由派生类及其以下版本使用,但不会更改基类的功能

鉴于

纯虚函数或纯虚方法是一种虚函数,如果派生类不是抽象的,则需要由派生类实现

当存在纯虚拟方法时,类是“抽象的”,不能单独实例化。相反,必须使用实现纯虚方法的派生类。基类中根本没有定义纯虚拟类,因此派生类必须定义它,或者该派生类也是抽象的,不能实例化。只能实例化没有抽象方法的类

<>虚拟提供了一种重写基类功能的方法,而纯虚拟需要它。

在C++类中,Virtual是指定该关键字的关键字,可以重写(即由子类实现)方法。例如:

class Shape 
{
  public:
    Shape();
    virtual ~Shape();

    std::string getName() // not overridable
    {
      return m_name;
    }

    void setName( const std::string& name ) // not overridable
    {
      m_name = name;
    }

  protected:
    virtual void initShape() // overridable
    {
      setName("Generic Shape");
    }

  private:
    std::string m_name;
};
在这种情况下,子类可以重写initShape函数来执行一些专门的工作:

class Square : public Shape
{
  public: 
    Square();
    virtual ~Square();

  protected:
    virtual void initShape() // override the Shape::initShape function
    {
      setName("Square");
    }
}
术语pure-virtual指的是需要由子类实现但尚未由基类实现的虚拟函数。通过使用virtual关键字并在方法声明的末尾添加a=0,可以将方法指定为纯虚拟方法

因此,如果您想使Shape::initShape纯虚拟,您可以执行以下操作:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};
通过向类中添加纯虚拟方法,可以使类成为
这对于分离接口和实现非常方便

>虚拟关键词赋予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();
}
它将输出“2”。如果我们这样做:

Duck d;
SomeFunction(&d);
Horse h;
SomeFunction(&h);
它将输出“4”。我们不能这样做:

Animal a;
SomeFunction(&a);
因为GetNumberOfLegs()虚拟函数是纯函数,所以它不会编译,这意味着它必须通过派生类(子类)来实现

纯虚拟函数主要用于定义:

a) 抽象类

这些是基类,您必须从中派生,然后实现纯虚函数

b) 接口


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

我想评论一下维基百科对虚拟的定义,这里有几个人重复了这一点。[在撰写此答案时,]Wikipedia将虚拟方法定义为可以在子类中重写的方法。[幸运的是,Wikipedia从那时起就被编辑过,现在它正确地解释了这一点。]这是错误的:任何方法,不仅仅是虚拟方法,都可以在子类中被重写。virtual所做的是为您提供多态性,即在运行时选择最派生的方法重写的能力

考虑以下代码:

#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}
派生重写了基的每一种方法:不仅是虚拟方法,而且是非虚拟方法

我们看到,当您有一个指向派生(bDerived)的基指针时,调用非虚拟将调用基类实现。这是在编译时解决的:编译器看到bDerived是一个Base*,而非virtual不是虚拟的,所以它在类Base上进行解析

但是,调用Virtual调用派生类实现。由于关键字virtual,方法的选择发生在运行时,而不是编译时。编译时这里发生的事情是编译器看到这是一个Base*,并且它正在调用一个虚拟方法,因此它插入了对vtable的调用,而不是对Base类的调用。此vtable在运行时实例化,因此运行时解析为最派生的覆盖

我希望这不会太令人困惑。简言之,任何方法都可以被重写,但只有虚拟方法提供多态性,即,运行时选择最派生的重写。然而,在实践中,重写非虚方法被认为是不好的做法,很少使用,所以很多人(包括编写维基百科文章的人)认为只有虚拟方法可以被重写。

< P> Simula、C++和C.*,默认使用静态方法绑定,程序员可以指定特定方法。
#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}
Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()=0;

};

class anotherClass:aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"hellow World";
    }

};
int main()
{
    //aClassWithPureVirtualFunction virtualObject;
    /*
     This not possible to create object of a class that contain pure virtual function
    */
    anotherClass object;
    object.sayHellow();
}
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()
    {
        cout<<"from base\n";
    }

};

class anotherClass:public aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"from derived \n";
    }

};
int main()
{
    aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
    baseObject->sayHellow();///call base one

    baseObject=new anotherClass;
    baseObject->sayHellow();////call the derived one!

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

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}
Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.