Вызов виртуальных функций внутренние конструкторы

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

В этом примере

Нет видимой ошибки интерфейса

Они объявили operandStack с неправильным типом.

В этом

http://www.raywenderlich.com/forums/viewtopic.php?f=2&t=3312

У них была опечатка в имени селектора

220
задан Cheers and hth. - Alf 8 July 2018 в 22:07
поделиться

7 ответов

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

C ++ FAQ Lite довольно подробно описывает это в разделе 23.7. Я предлагаю прочитать это (и остальную часть часто задаваемых вопросов) для продолжения.

Отрывок:

[...] В конструкторе механизм виртуального вызова отключен, так как переопределение из производных классов еще не произошло. Объекты строятся от основания вверх, «основание перед производным».

[...]

Уничтожение выполняется «производный класс перед базовым классом», поэтому виртуальные функции ведут себя так же, как в конструкторах: используются только локальные определения - и никакие вызовы переопределяющих функций не выполняются, чтобы не касаться ( теперь уничтожена) часть производного класса объекта.

EDIT Исправлено больше всего (спасибо, литб)

204
ответ дан 23 November 2019 в 04:08
поделиться

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

-4
ответ дан 23 November 2019 в 04:08
поделиться

Сделать вы знаете ошибку вылета из проводника Windows ?! "Чистый вызов виртуальной функции ..."
Та же проблема ...

class AbstractClass 
{
public:
    AbstractClass( ){
        //if you call pureVitualFunction I will crash...
    }
    virtual void pureVitualFunction() = 0;
};

Поскольку функция pureVitualFunction () не реализована, а функция вызывается в конструкторе, программа завершится аварийно.

1
ответ дан 23 November 2019 в 04:08
поделиться

Одним из решений вашей проблемы является использование фабричных методов для создания вашего объекта.

  • Определите общий базовый класс для вашей иерархии классов, содержащий виртуальный метод afterConstruction ():
class Object
{
public:
  virtual void afterConstruction() {}
  // ...
};
  • Определите фабрику метод:
template< class C >
C* factoryNew()
{
  C* pObject = new C();
  pObject->afterConstruction();

  return pObject;
}
  • Используйте это так:
class MyClass : public Object 
{
public:
  virtual void afterConstruction()
  {
    // do something.
  }
  // ...
};

MyClass* pMyObject = factoryNew();

13
ответ дан 23 November 2019 в 04:08
поделиться

C ++ FAQ Lite охватывает это довольно хорошо:

По сути, во время вызова конструктора базовых классов объект еще не относится к производному типу и, следовательно, вызывается реализация виртуальной функции базового типа, а не производного типа.

23
ответ дан 23 November 2019 в 04:08
поделиться

Причина в том, что объекты C ++ строятся как луковицы изнутри. Суперклассы создаются раньше производных классов. Итак, прежде чем можно будет сделать B, необходимо сделать A. Когда вызывается конструктор A, он еще не является B, поэтому в таблице виртуальных функций все еще есть запись для копии A для fn ().

56
ответ дан 23 November 2019 в 04:08
поделиться

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

Основная проблема заключается в том, что на всех языках базовый тип (ы) должен быть сконструирован до производного типа. Теперь проблема в том, что означает вызов полиморфного метода из конструктора. Как вы ожидаете, что он будет вести себя? Существует два подхода: вызов метода на базовом уровне (стиль C ++) или вызов полиморфного метода для неструктурированного объекта в нижней части иерархии (способ Java).

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

class Base {
public:
   Base() { f(); }
   virtual void f() { std::cout << "Base" << std::endl; } 
};
class Derived : public Base
{
public:
   Derived() : Base() {}
   virtual void f() { std::cout << "Derived" << std::endl; }
};
int main() {
   Derived d;
}
// outputs: "Base" as the vtable still points to Base::f() when Base::Base() is run

В Java компилятор построит виртуальную таблицу эквивалент на самом первом шаге построения, перед входом в конструктор Base или конструктор Derived. Последствия разные (и, на мой взгляд, опаснее). Если конструктор базового класса вызывает метод, который переопределяется в производном классе, вызов фактически будет обрабатываться на производном уровне, вызывая метод для неструктурированного объекта, дающие неожиданные результаты. Все атрибуты производного класса, которые инициализированы внутри блока конструктора, еще не инициализированы, включая атрибуты final. Элементы, для которых значение по умолчанию определено на уровне класса, будут иметь это значение.

public class Base {
   public Base() { polymorphic(); }
   public void polymorphic() { 
      System.out.println( "Base" );
   }
}
public class Derived extends Base
{
   final int x;
   public Derived( int value ) {
      x = value;
      polymorphic();
   }
   public void polymorphic() {
      System.out.println( "Derived: " + x ); 
   }
   public static void main( String args[] ) {
      Derived d = new Derived( 5 );
   }
}
// outputs: Derived 0
//          Derived 5
// ... so much for final attributes never changing :P

Как видите, вызов полиморфных ( виртуальных в терминологии C ++) методов является частым источником ошибок. В C ++, по крайней мере, у вас есть гарантия, что он никогда не вызовет метод еще не созданного объекта ...

вызов полиморфных ( виртуальных в терминологии C ++) методов - частый источник ошибок. В C ++, по крайней мере, у вас есть гарантия, что он никогда не вызовет метод еще не созданного объекта ...

вызов полиморфных ( виртуальных в терминологии C ++) методов - частый источник ошибок. В C ++, по крайней мере, у вас есть гарантия, что он никогда не вызовет метод еще не созданного объекта ...

80
ответ дан 23 November 2019 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: