clooneljump/docs/wrong_virtual_call.md
King_DuckZ 5e76b49cae Use the deferred virtual call mechanism to avoid the virtual
call bug in the constructor.

See docs/wrong_virtual_call.md for a description.
2016-11-10 23:19:55 +01:00

1.3 KiB

  1. SizeNotifiable, defined as the following:

    class SizeNotifiable<RegisterBehaviour, true> : private RegisterBehaviour, public SizeNotifiableBase { ... };

gets constructed - it inherits from SizeNotifiableBase which declares NotifyResChanged() as virtual. It also inherits from RegisterBehaviour, which must provide a Register() method.

  1. In SizeNotifiable's constructor RegisterBehaviour::Register(this) gets called. Being in the constructor, virtual calls on this are UB.

  2. When RegisterBehaviour == AutoRegister, we have Register defined as the following:

    void AutoRegister::Register (SizeNotifiableBase* parNotifiable) { m_id = m_sdlmain->RegisterForResChange(parNotifiable); }

note that parNotifiable is received from SizeNotifiable's constructor, so it's a partially constructed object.

  1. RegisterForResChange() is defined as:

    size_t SDLMain::RegisterForResChange (SizeNotifiableBase* parNotif) { parNotif->NotifyResChanged(m_localData->sizeratio); return m_localData->resChangeNotifList.Add(parNotif); }

parNotif is, again, the partly constructed object pointed by this in SizeNotifiable's constructor. NotifyResChanged() is a virtual method, thus the bug. Objects being registered need to be notified straight away since they have no clue about the current size held by SDLMain.