Вызов виртуальной функции и чисто виртуальной функции из конструктора
Когда я вызываю виртуальную функцию из базового конструктора, компилятор не выдает никакой ошибки. Но когда я вызываю чисто виртуальную функцию из конструктора базового класса, это дает ошибку компиляции.
Рассмотрим пример программы ниже:
#include <iostream>
using namespace std;
class base
{
public:
void virtual virtualfunc() = 0;
//void virtual virtualfunc();
base()
{
virtualfunc();
}
};
void base::virtualfunc()
{
cout << " pvf in base class\n";
}
class derived : public base
{
public:
void virtualfunc()
{
cout << "vf in derived class\n";
}
};
int main()
{
derived d;
base *bptr = &d;
bptr->virtualfunc();
return 0;
}
Здесь видно, что чисто виртуальная функция имеет определение. Я ожидал, что чистая виртуальная функция, определенная в базовом классе, будет вызвана, когда bptr->virtualfunc()
выполнен. Вместо этого он дает ошибку компиляции:
ошибка: абстрактное виртуальное `virtual void base::virtualfunc()'вызывается из конструктора
Что является причиной этого?
4 ответа
Не вызывайте чисто виртуальные функции из конструктора, так как это приводит к неопределенному поведению.
C++ 03 10,4/6 состояний
"Функции-члены могут вызываться из конструктора (или деструктора) абстрактного класса; эффект от виртуального вызова (10.3) чистой виртуальной функции прямо или косвенно для объекта, создаваемого (или уничтожаемого) из такого конструктора (или деструктор) не определено. "
Вы получаете ошибку компиляции, потому что вы не определили чисто виртуальную функцию virtualfunc()
в базовом классе. Чтобы это можно было назвать, у него должно быть тело.
В любом случае, следует избегать вызова чисто виртуальных функций в конструкторах, так как это неопределенное поведение.
В C++ 11 есть обходной путь.
В процессе делегирования конструктора вы можете вызвать реализацию чисто виртуального метода - при условии, что хотя бы один из конструкторов класса, реализующего этот чисто виртуальный метод, был завершен до вызова чисто виртуального вызова.,
Вы можете / должны использовать ключевое слово "final", чтобы гарантировать, что поведение подклассов не будет непредсказуемым.
См.: C++ 11 Делегированный конструктор Чистый виртуальный вызов методов и функций - опасность?
#include <string>
/**************************************/
class Base
{
public:
int sum;
virtual int Do() = 0;
void Initialize()
{
Do();
}
Base()
{
}
};
/**************************************/
// Optionally declare class as "final" to avoid
// issues with further sub-derivations.
class Derived final : public Base
{
public:
virtual int Do() override final
{
sum = 0 ? 1 : sum;
return sum / 2 ; // .5 if not already set.
}
Derived(const std::string & test)
: Derived() // Ensure "this" object is constructed.
{
Initialize(); // Call Pure Virtual Method.
}
Derived()
: Base()
{
// Effectively Instantiating the Base Class.
// Then Instantiating This.
// The the target constructor completes.
}
};
/********************************************************************/
int main(int args, char* argv[])
{
Derived d;
return 0;
}
Вы должны помнить, что когда вы находитесь в конструкторе базового класса, не существует производного класса. Дополнительная информация:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html
Когда вы пытаетесь вызвать чисто виртуальную функцию, ее реализация пока не реализована.
Есть много решений. Самый простой - создать другую функцию-член "init()", которую вы будете вызывать после конструктора базового класса.
Компилятору не требуется предполагать, что указатель был установлен для чисто виртуальной функции до завершения работы конструктора. Другими словами, не обязательно знать, что у вас есть определение для функции на этом этапе. Поэтому поведение не определено. На некоторых компиляторах (MSVC) он будет работать так, как вы ожидаете, а на других он выдаст вам ошибку, которую вы сейчас получаете. На некоторых других он будет компилироваться, но вы получите ошибку сегментации.
В любом случае, это плохая идея - вызывать ЛЮБУЮ виртуальную функцию из конструктора, потому что это делает смысл вашего кода неясным и запутанным.