чисто - таблица виртуальных функций c++




Получить производный тип через виртуальную функцию базового класса (4)

C ++ поддерживает ковариантный тип возвращаемого значения. Это означает, что когда вы вызываете get_this() на derived объекте с помощью base указателя, это реализация производного, который будет вызываться.

Однако это не означает, что вызов base::get_this даст вам derived& . Возвращаемый тип base::get_this является base& . если вы хотите получить derived объект, вам придется вызывать get_this через derived указатель (или понижать вашу base& до derived& ). Обратите внимание, что это то, как ковариация возвращаемого типа работает в Java, C ++, D ...

base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();

base& a = pbase->get_this();        // call implementation in base, return base&
base& b = pderived->get_this();     // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&

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

struct base {
  virtual base& get_this() {
    return *this;
  }
};

struct derived : base {
  virtual derived& get_this() override {
    return *this;
  }

  void fn();
};


int main () {
  base* pd = new derived();
  derived& x = pd->get_this(); /*ERROR*/
  x.fn();
  return 0;
}

... давая мне ошибку: я не могу инициализировать derived& из base . Поскольку get_this является виртуальным, почему pd->get_this() возвращает base& вместо derived& ? Заранее спасибо!

РЕДАКТИРОВАТЬ:

Спасибо всем за их полезные ответы и извинения за мой поздний ответ. Я должен был указать в первоначальном сообщении, что меня также интересует решение моей проблемы, а не просто выяснение, почему приведенное выше не компилируется. Моя основная проблема заключается в том, что fn уникален для derived класса и не может быть вызван через базовый класс. Использование бросков наверняка решает проблему, но я ненавижу писать код, если else строит только для получения правильного типа (также Скотт Мейерс советует против бросков :)). Ответы, похоже, указывают на то, что броски - это путь, который, по крайней мере, обнадеживает, что я не пренебрегаю более «элегантным» решением моей проблемы. Еще раз спасибо!


Статический тип pd является base * . Таким образом, когда компилятор ищет функцию-член get_this() , он находит только base::get_this() . Возвращаемый тип base::get_this() является base& , которая не конвертируется в derived& . Отсюда и ошибка.


Я нашел простое решение, но если это возможно, я бы оценил мастера:

class base{ 
    type = 1;
    virtual int getType() final {
        return type;
    }
}

class derived1 : public base {
    derived1(){
        type = 2;
    }
}

Таким образом, вы можете вызвать метод 'int getType ()' любого из производных классов. Поскольку тип установлен на конструкторе, нет никакого риска неправильного поведения. Чтобы повысить удобство использования, я создал предопределенные «типы».

Я использую, но я не знаю, MacGyvery!


Поддержка типов Covariant возвращаемых типов C ++ будет работать только до тех пор, пока вы уже знаете производный тип. Чтобы понизить базовый класс до возможно производного класса, просто используйте dynamic_cast<derived>(base_ref) чтобы определить, соответствует ли base_ref фактическому производному типу:

int main () {
    base* pd = new derived();
    derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd 
                                          // isn't a 'derived'
    x.fn();
    return 0;
}

Или, альтернативно:

int main () {
    base* pd = new derived();
    derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn't
                                         // a 'derived'
    if(x) {
        x->fn();
    }
    else {
        // dynamic_cast<derived*> failed ...
    }
    return 0;
}

c ++ поддерживает ковариантные типы возвращаемых данных для производных классов, но, как pd->get_this() другие ответы, вы не можете получить его через вызов базового класса ( pd->get_this() ) здесь.

Вы также можете рассмотреть статический полиморфизм, чтобы проверить соответствие типа во время компиляции, если вы не можете использовать RTTI , обработку исключений или хотите жесткую привязку типа (без накладных расходов vtable).







derived