Вызов виртуальной функции и чисто виртуальной функции из конструктора - PullRequest
5 голосов
/ 27 декабря 2011

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

Рассмотрим пример программы ниже:

#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 ]

10 голосов
/ 27 декабря 2011

Не вызывайте чисто виртуальные функции из конструктора, так как это приводит к Неопределенное поведение .

C ++ 03 10.4 / 6 состояний

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

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

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

2 голосов
/ 04 февраля 2013

В C ++ 11 есть обходной путь.

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

Вы можете / должны использовать ключевое слово "final", чтобы гарантировать, что поведение подклассов не будет непредсказуемым.

См .: C ++ 11 Вызовы делегированного конструктора Pure Virtual и вызовы функций - опасности?

#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;
}
1 голос
/ 27 декабря 2011

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

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

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

Существует множество решений. Самый простой - создать другую функцию-член "init ()", который вы будете вызывать после конструктора базового класса.

0 голосов
/ 27 декабря 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...