#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, потому что существует чисто виртуальный метод.