Как вызвать переопределенную виртуальную функцию из функции базового класса?

#c #inheritance #pure-virtual

#c #наследование #чисто виртуальный

Вопрос:

Я только что понял, что слишком сильно упростил код и что он не отражает мою реальную проблему. Я прошу прощения за то, что не был более конкретным. На самом деле я пытаюсь сделать следующее:

онлайн-демонстрация:

 #include<iostream>

class A
{
    public:
        A();

        virtual void f()= 0;
        void g();
};

A::A()
{
    g();
}

void A::g()
{
    f();
}

class B : public A
{
    public:
        B() {};
        void f() {};
};

int main()
{
    B b;
    return 0;
}
  

Я предполагаю, что программа вызывает чисто виртуальную функцию, A::f поскольку B она еще не была создана при вызове конструктора.

Правильно ли это и как я могу преодолеть эту проблему?

Пожалуйста, простите меня за то, что я привел слишком упрощенную проблему ранее.

Комментарии:

1. Я тоже жду ответа. Насколько я знаю о C , вы не можете создать экземпляр класса, в котором класс все еще имеет некоторые нереализованные чисто виртуальные методы..

2. В коде, который вы удалили, почти наверняка есть ошибка.

3. Нет, это определенно не компилируется. Возможно, какая-то связанная программа действительно компилируется. Если да, пожалуйста, покажите это дословно.

4. @Heisenbug 1, мне было интересно, как оригинальный poster также скомпилировал ее

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

Ответ №1:

Рабочий образец вашего кода, после того, как я удалил множество других ошибок.

 #include<iostream>

class A 
{
    virtual void f()=0;
    public:
    void g();
};

void A::g() 
{
   f();
}

class B : public A 
{

    void f(){std::cout<<"Inside B::f";}
};

int main() 
{
    B b;
    b.g();
    return 0;
}
  

Вывод:

Внутри B::f

Опубликуйте свой реальный код, чтобы получить реальные ответы.


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

Когда вы создаете объект класса, B используя B b; , это приводит к вызову конструктора базового класса A::A() , который, в свою очередь, вызывает метод g() . Обратите внимание, что g() это всего лишь метод базового класса, поэтому это приводит к вызову A::g() . Это дополнительные вызовы f() . Теперь вы ожидаете, что это должно вызвать B::f() , но этого не происходит.

ПОЧЕМУ?
Обратите внимание на правило,
Внутри конструктора или деструктора тип объекта, на который this указывает, всегда является типом, конструктор / деструктор которого вызывается.

Применяя вышеупомянутое правило, поскольку f() вызывается в конструкторе A ,
this->f() вызывает A::f() и не B::f() вызывает. Кроме того, поскольку A::f() не имеет определения (поскольку вы его не предоставили) Это приводит к исключению во время выполнения:

чисто виртуальный метод с именем terminate вызывается без активного исключения.

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

Как я могу преодолеть эту проблему?
Вы не можете сделать это с помощью динамической отправки в конструкторе.
Вы можете достичь и ожидать ожидаемого поведения, если вы вызвали его из любого другого метода, кроме конструктора / деструктора.

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

Ответ №2:

Если сигнатура функции B:: f соответствует A:: f, то использование f() в A:: g должно вызывать B:: f так, как вы этого хотите. В вашем примере выше они совпадают, и A:: g должен вызывать B::f .

Если сигнатуры вашей реальной функции более сложны, чем приведенный выше пример, убедитесь, что их сигнатуры точно совпадают, иначе компилятор будет считать B:: f новой функцией вместо переопределения A ::f .

*: строго говоря, возвращаемый тип может немного отличаться, но я полагаю, что здесь это не имеет значения

Ответ №3:

После устранения ошибок компилятора ваш код работает нормально.

Ответ №4:

Концептуально это должно сработать. Я не был в C целую вечность, но это не должно сильно отличаться от этого сценария в C#:

 public class MyBase
{
    public virtual void PrintName() { }
    public void DoWork()
    {
        PrintName();
    }
}

public class MyDerivative : MyBase
{
    public override void PrintName()
    {
        Console.WriteLine("MyDerivative");
    }
}
  

Комментарии:

1. Конечно, в C # вы бы отметили MyBase и MyBase::printName как абстрактные.

Ответ №5:

 #include <iostream>

class A {
public:
    virtual void f() = 0;
    void g();
};

void A::g() {
    std::cout << "A::g" << std::endl;
    this->f();
}

class B : public A {
public:
    virtual void f();
};

void B::f()
{
    std::cout << "B::f" << std::endl;
}

int main() {
    B b;
    b.g();
    return 0;
}
  

Ответ №6:

добавьте это в исходный код :

 void B::f()
{
    printf("im B::fn");
}
  

и ваша программа должна работать.

вы не можете создавать экземпляры A, потому что существует чисто виртуальный метод.