Виртуальный/чистый виртуальный объясненный

Что точно это означает, определяется ли функция как виртуальная, и это - то же как чистое виртуальный?

326
задан Uwe Keim 30 July 2019 в 19:02
поделиться

5 ответов

Из Виртуальная функция Википедии ...

В объектно-ориентированном программировании, на таких языках, как C ++ и Object Pascal, виртуальная функция или виртуальный метод является наследуемой и переопределяемой функцией или методом, для которых упрощается динамическая диспетчеризация. Эта концепция является важной частью полиморфизма (времени выполнения) объектно-ориентированного программирования (ООП). Короче говоря, виртуальная функция определяет целевую функцию, которая должна быть выполнена, но цель может быть неизвестна во время компиляции.

В отличие от невиртуальной функции, когда виртуальная функция переопределяется, используется самая производная версия. уровни иерархии классов, а не только уровень, на котором он был создан. Следовательно, если один метод базового класса вызывает виртуальный метод, версия, определенная в производном классе, будет использоваться вместо версии, определенной в базовом классе.

В этом отличие от невиртуальных функций, которые все еще можно переопределить в производном классе, но «новая» версия будет использоваться только производным классом и ниже, но не изменит функциональность базового класса. вообще.

тогда как ..

Чистая виртуальная функция или чистый виртуальный метод - это виртуальная функция, которая должна быть реализована производным классом, если производный класс не является абстрактным.

Когда чистый виртуальный метод существует, класс является «абстрактным» и не может быть создан сам по себе. Вместо этого должен использоваться производный класс, реализующий чисто виртуальный метод (ы). Чистый виртуальный объект вообще не определен в базовом классе, поэтому производный класс должен определять его, или этот производный класс также является абстрактным и не может быть создан.

319
ответ дан 23 November 2019 в 00:51
поделиться

Ключевое слово 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, поскольку это базовый класс.

Если мы сделаем это:

Duck d;
SomeFunction(&d);

, он выведет «2». Если мы сделаем это:

Horse h;
SomeFunction(&h);

, будет выведено '4'. Мы не можем этого сделать:

Animal a;
SomeFunction(&a);

, потому что он не будет компилироваться из-за того, что виртуальная функция GetNumberOfLegs () является чистой, что означает, что она должна быть реализована путем наследования классов (подклассов).

112
ответ дан 23 November 2019 в 00:51
поделиться

В классе 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");
    }
}

Термин чисто виртуальный относится к виртуальным функциям, которые должны быть реализованы подклассом и не реализованы базовым классом. Вы определяете метод как чисто виртуальный, используя ключевое слово virtual и добавляя = 0 в конце объявления метода.

Итак, если вы хотите сделать Shape :: initShape чисто виртуальным, вы должны сделать следующее:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

Добавляя чистый виртуальный метод в свой класс, вы делаете класс абстрактным базовым классом что очень удобно для отделения интерфейсов от реализации.

30
ответ дан 23 November 2019 в 00:51
поделиться

«Виртуальный» означает, что метод может быть переопределен в подклассах, но имеет напрямую вызываемую реализацию в базовом классе. «Чистый виртуальный» означает, что это виртуальный метод без реализации, вызываемой напрямую. Такой метод должен быть переопределен хотя бы один раз в иерархии наследования - если у класса есть какие-либо нереализованные виртуальные методы, объекты этого класса не могут быть построены и компиляция завершится неудачно.

@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;
}

Согласно комментариям, завершится ли компиляция сбой, зависит от компилятора. По крайней мере, в GCC 4.3.3 он победил ' t compile:

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()
15
ответ дан 23 November 2019 в 00:51
поделиться

Я хотел бы прокомментировать определение виртуальности в Википедии, которое некоторые здесь повторяют. [На момент написания этого ответа] Википедия определила виртуальный метод как метод, который можно переопределить в подклассах. [К счастью, с тех пор Википедия была отредактирована, и теперь она правильно объясняет.] Это неверно: любой метод, не только виртуальный, можно переопределить в подклассах. Что действительно делает 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();
}

Что будет на выходе этой программы?

Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.

Derived переопределяет каждый метод Base: не только виртуальный, но и не виртуальный.

Мы видим, что когда у вас есть Base-указатель на Derived (bDerived), вызывающий NonVirtual вызывает реализацию базового класса. Это разрешается во время компиляции: компилятор видит, что bDerived является Base *, что NonVirtual не является виртуальным, поэтому он выполняет разрешение в классе Base.

Однако вызов Virtual вызывает реализацию класса Derived. Из-за ключевого слова virtual выбор метода происходит во время выполнения , а не во время компиляции. Что здесь происходит во время компиляции, так это то, что компилятор видит, что это Base * и что он вызывает виртуальный метод, поэтому он вставляет вызов в vtable вместо класса Base. Эта vtable создается во время выполнения, отсюда разрешение времени выполнения до наиболее часто используемого переопределения.

Надеюсь, это не слишком запутало. Короче говоря, любой метод можно переопределить, но только виртуальные методы дают вам полиморфизм, то есть выбор во время выполнения наиболее производного переопределения. Однако на практике

201
ответ дан 23 November 2019 в 00:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: