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



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

725   12  
c++

12 ответов:

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

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

в то время как..

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

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

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

мы видим, что когда у вас есть базовый указатель на производное (bDerived), вызов невиртуальных вызовов реализации базового класса. Это решается во время компиляции: компилятор видит, что bDerived является базой*, что Невиртуальный не является виртуальным, поэтому он выполняет разрешение на основе класса.

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

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

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

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

Duck d;
SomeFunction(&d);

Он выведет '2'. Если мы сделаем это:

Horse h;
SomeFunction(&h);

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

Animal a;
SomeFunction(&a);

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

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

a) аннотация классы

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

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

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

в классе C++, виртуальный - это ключевое слово, которое указывает, что метод может быть переопределен (т. е. осуществляемые) подкласс. Например:

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");
    }
}

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

Итак, если вы хотите сделать Shape:: initShape pure virtual, вы сделаете следующее:

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

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

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

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

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

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

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 (), Man's do_work () вызывается, даже если он указывает на индийский объект

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

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

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

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

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

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

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

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

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

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

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

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

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

Чисто Виртуальная Функция

попробуйте этот код

#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();
}

в классе anotherClass удалите функцию 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!

}

здесь функция sayHellow помечена как виртуальная в базе class.It скажем компилятор, который пытается найти функцию в производном классе и реализовать функцию.Если не найден, то выполните базовый.Спасибо

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

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

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

  • виртуальные функции должны иметь определение в базовом классе, а также в производном классе, но не обязательно, например ToString() или toString() функция является виртуальной, поэтому вы можете предоставить свою собственную реализацию, переопределив ее в пользовательском классе(классах).

  • виртуальные функции объявляются и определяются в нормальный класс.

  • чистая виртуальная функция должна быть объявлена с окончанием "= 0", и она может быть объявлена только абстрактно класс.

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

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

Comments

    Ничего не найдено.