С ++: вызов чистой виртуальной функции из функции-члена того же класса

Рассмотрим следующие 2 программы.

#include <iostream>
using std::cout;
class Base {
    public:
        virtual void f()=0;
        void g() {
            f();
        }
        virtual ~Base() { }
};
class Derived : public Base
{
    public:
    void f() {
        cout<<"Derived::f() is called\n";
    }
     ~Derived() {}
};
class Derived1 : public Base
{
    public:
    void f() {
        cout<<"Derived1::f() is called\n";
    }
    ~Derived1() { }
};
int main() {
    Derived1 d;
    Base& b=d;
    b.g();
    b.f();
}

Компилируется и работает нормально и дает ожидаемый результат.

#include <iostream>
using std::cout;
class Base {
    public:
        virtual void f()=0;
        Base() {
            f();    // oops,error can't call from ctor & dtor
        }
};
class Derived : public Base
{
    public:
        void f() {
            std::cout<<"Derived::f() is called\n";
        }
};
int main() { Derived d; Base& b=d; b.f(); }

Вышеупомянутая программа не компилируется. Почему разрешено вызывать чистую виртуальную функцию из функции-члена того же класса, в котором объявлена ​​чистая виртуальная функция? Это нормально, или это поведение undefined, потому что производный класс по-прежнему не обеспечивает реализацию чистой виртуальной функции? Почему нельзя вызвать чистую виртуальную функцию из конструктора и деструктора того же класса? Я знаю, что конструкторы производных классов могут вызывать чистые виртуальные функции базового класса. Что об этом говорит стандарт C ++?


person Destructor    schedule 23.05.2015    source источник
comment
Связанный: stackoverflow.com/q/962132/3093378   -  person vsoftco    schedule 23.05.2015
comment
@vsoftco: эта ссылка обсуждает другую тему.   -  person Destructor    schedule 23.05.2015
comment
второй ответ касается чего-то очень похожего, а не того же, поэтому я сказал, что это связано.   -  person vsoftco    schedule 23.05.2015


Ответы (2)


«Почему чистая виртуальная функция не может быть вызвана из конструктора ...?»

Поскольку последний класс на этом этапе построен не полностью, а vtable не полностью настроен для правильной отправки вызова функции.


В качестве альтернативы вы можете использовать отношение static для базового и производного классов, как это предлагается с CRTP:

template<class DerivedType>
class Base {
    public:
        void g() {
            static_cast<DerivedType*>(this)->f();
        }
        virtual ~Base() { }
};

class Derived : public Base<Derived>
{
    public:
    void f() {
        cout<<"Derived::f() is called\n";
    }
     ~Derived() {}
};

class Derived1 : public Base<Derived1>
{
    public:
    void f() {
        cout<<"Derived1::f() is called\n";
    }
    ~Derived1() { }
};
person πάντα ῥεῖ    schedule 23.05.2015
comment
Да ! И это сделано специально, потому что сначала должен быть построен базовый класс. - person Christophe; 23.05.2015
comment
Хороший! Я думаю, вы также можете использовать if(dynamic_cast<DerivedType*>(this)) dynamic_cast<DerivedType*>(this)->f(); для безопасности, так как он поймает Base<Derived>* pBase = new Base<Derived>; pBase->f();, что в противном случае даст вам UB. - person vsoftco; 23.05.2015

Почему разрешено вызывать чистую виртуальную функцию из функции-члена того же класса, в котором объявлена ​​чистая виртуальная функция?

Потому что это технически осуществимо и используется на практике: см. Шаблон "Шаблонный метод".

Почему чистая виртуальная функция не может быть вызвана из конструктора и деструктора того же класса?

Технически это непросто реализовать на C ++ (пока нет vtable). Но, что более важно, он вам все равно не понадобится, поскольку вы всегда знаете точный класс вашего объекта при вызове конструктора.

person Bérenger    schedule 23.05.2015
comment
Это вполне технически осуществимо - и не работает. Это то, что делает Java. - person T.C.; 23.05.2015
comment
@ T.C. Отредактировано с учетом вашего комментария. - person Bérenger; 23.05.2015