Как реализовать чистую виртуальную функцию в C++

Я думаю, что о реализации виртуальной функции говорят много. Мой вопрос что относительно чистой виртуальной функции? Однако это реализовано? В виртуальной таблице, как сказать, это - чистое или нечистое? Что различие между чистой виртуальной функцией и виртуальной функцией с реализацией?

5
задан skydoor 11 March 2010 в 21:08
поделиться

4 ответа

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

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

4
ответ дан 14 December 2019 в 01:06
поделиться

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

Это настолько логично, что я даже думаю, что это реализовано таким образом :-).

0
ответ дан 14 December 2019 в 01:06
поделиться

virtual void foo () = 0;

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

Это помогает?

3
ответ дан 14 December 2019 в 01:06
поделиться

Это не ответ, а скорее продолжение комментариев к этот ответ выше

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

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

struct base {
   virtual void non_pure() { std::cout << "base::non_pure" << std::endl; }
   virtual void pure_not_implemented() = 0;
   virtual void pure_implemented() = 0;

   base() {                      // at this point the object is a ´base´
      non_pure();                // base::non_pure
      // pure_not_implemented(); // runtime error: pure virtual method called
      pure_implemented();        // base::pure_implemented
      // base::pure_not_implemented(); // link error
   }
};
void base::pure_implemented() { std::cout << "base::pure_implemented" << std::endl; }
struct derived : base {
   virtual void non_pure() { std::cout << "derived::non_pure" << std::endl; }
   virtual void pure_not_implemented() { std::cout << "derived::pure_not_implemented" << std::endl; }
   virtual void pure_implemented() { std::cout << "derived::pure_implemented" << std::endl;

   derived() {                // after the implicit call to the base class
                              //   this is a ´derived´ object, now calls will
                              //   get dispatched to derived:: implementations
      non_pure();             // derived::non_pure
      pure_not_implemented(); // derived::pure_not_implemented
      pure_implemented();     // derived::pure_implemented
      base::non_pure();       // base::non_pure
      // base::pure_not_implemented() // link error
   }
};

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

Это важное решение при разработке языка.Другой вариант (тот, который выбрала Java) - инициализация механизма виртуальной диспетчеризации наиболее производным типом с самого начала, до вызова конструктора базового класса. Проблема этого подхода заключается в том, что если базовый конструктор вызывает виртуальный метод (который в Java есть у всех), он будет отправлен в наиболее производную реализацию и, таким образом, будет выполняться в еще не созданном объекте, что может привести к неожиданным результатам:

public class Base
{
    public Base() {
        f();
    }
    public void f() {
        System.out.println("Base.f");
    }
}
public class Derived extends Base {
    public final int constant;
    public Derived() { constant = 5; }
    public void f() {
        System.out.println( "Derived.f() " + constant );
    }
    public static void main( String args[] ) {
        Derived d = new Derived();  // prints Derived.f() 0
    }
}

В версии Java механизм динамической отправки с самого начала считает, что объект имеет тип Derived . Вызов f () в базовом конструкторе будет динамически отправлен производной реализации. В приведенном выше примере, даже если переменная объявлена ​​как final и, следовательно, является константой со значением 5 (кажется очевидным в коде), фактическое напечатанное значение равно 0, потому что конструктор Derived не выполнил .

(*) Это чрезмерное упрощение, но детали на самом деле не влияют на аргумент.

2
ответ дан 14 December 2019 в 01:06
поделиться
Другие вопросы по тегам:

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