C ++ Virtual / Pure Virtual объяснил - PullRequest
       34

C ++ Virtual / Pure Virtual объяснил

316 голосов
/ 20 августа 2009

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

Ответы [ 12 ]

310 голосов
/ 20 августа 2009

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

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

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

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

тогда ..

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

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

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

193 голосов
/ 20 августа 2009

Я хотел бы прокомментировать определение виртуального в Википедии, как повторяют некоторые здесь. [На момент написания этого ответа] Википедия определила виртуальный метод как метод, который можно переопределить в подклассах. [К счастью, с тех пор Википедия была отредактирована, и теперь она объясняет это правильно.] Это неверно: любой метод, не только виртуальный, может быть переопределен в подклассах. Что виртуальное делает, так это дает вам полиморфизм, то есть способность выбирать во время выполнения наиболее производное переопределение метода .

Рассмотрим следующий код:

#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.

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

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

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

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

111 голосов
/ 20 августа 2009

Виртуальное ключевое слово дает 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 () является чистой, что означает, что она должна быть реализована путем получения классов (подклассов).

Чистые виртуальные функции в основном используются для определения:

а) абстрактные классы

Это базовые классы, из которых вы должны извлечь их, а затем реализовать чисто виртуальные функции.

б) интерфейсы

Это «пустые» классы, где все функции являются чисто виртуальными, и, следовательно, вы должны получить и затем реализовать все функции.

30 голосов
/ 20 августа 2009

В классе 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 и добавляя = 0 в конце объявления метода.

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

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

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

15 голосов
/ 20 августа 2009

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

@ 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 он не будет компилироваться:

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()
9 голосов
/ 24 января 2014

Как работает виртуальное ключевое слово?

Предположим, что человек является базовым классом, индиец происходит от человека.

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

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

Объявление do_work () как виртуального просто означает, что do_work () для вызова будет определено ТОЛЬКО во время выполнения.

Предположим, я делаю,

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

Если virtual не используется, то же самое статически определяется или статически связывается компилятором, в зависимости от того, какой объект вызывается. Так что если объект Man вызывает do_work (), то do_work () Man называется ДАЖЕ, ЧТО ЭТО УКАЗЫВАЕТ НА ИНДИЙСКИЙ ОБЪЕКТ

Я полагаю, что ответ с наибольшим количеством голосов вводит в заблуждение. Любой метод, независимо от того, является ли виртуальный, может иметь переопределенную реализацию в производном классе. Что касается конкретной ссылки на C ++, то правильной разницей является привязка во время выполнения (когда используется виртуальная) и привязка во время компиляции (когда виртуальная не используется, но метод переопределен, а базовый указатель указывает на производный объект), привязка связанных функций.

Кажется, есть еще один вводящий в заблуждение комментарий, в котором говорится:

«Джастин,« чисто виртуальный »- это просто термин (не ключевое слово, см. Мой ответ ниже) используется для обозначения "эта функция не может быть реализована с помощью базы класс. "

ЭТО НЕПРАВИЛЬНО! Чисто виртуальные функции также могут иметь тело и могут быть реализованы! Правда в том, что чисто виртуальную функцию абстрактного класса можно вызывать статически! Два очень хороших автора - Бьярн Страуструп и Стэн Липпман ... потому что они написали язык.

2 голосов
/ 04 мая 2010

Simula, C ++ и C #, которые по умолчанию используют статическое связывание методов, программист может указать, что определенные методы должны использовать динамическое связывание, пометив их как виртуальные. Привязка динамических методов занимает центральное место в объектно-ориентированном программировании.

Объектно-ориентированное программирование требует трех основных понятий: инкапсуляция, наследование и динамическое связывание методов.

Инкапсуляция позволяет реализовать детали реализации абстракция, которая будет спрятана за простой интерфейс

Наследование позволяет определять новую абстракцию как расширение или уточнение некоторых существующая абстракция, получение некоторых или все его характеристики автоматически.

Динамическая привязка метода позволяет новой абстракции отображать свою новую поведение даже при использовании в контексте что ожидает старую абстракцию.

1 голос
/ 24 июня 2014

Виртуальные методы МОГУТ быть переопределены путем получения классов, но нуждаются в реализации в базовом классе (тот, который будет переопределен)

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

Virtual соответствует поведению Java по умолчанию, когда производный класс переопределяет метод базового класса.

Чисто Виртуальные методы соответствуют поведению абстрактных методов внутри абстрактных классов. И класс, который содержит только чистые виртуальные методы и константы, будет cpp-pendant для интерфейса.

0 голосов
/ 07 марта 2017

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

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

0 голосов
/ 14 сентября 2013

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

Это не очень хорошее объяснение для виртуальных функций. Потому что, даже если член не является виртуальным, наследующие классы могут переопределить его. Вы можете попробовать и увидеть это сами.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...