Use the deferred virtual call mechanism to avoid the virtual

call bug in the constructor.

See docs/wrong_virtual_call.md for a description.
This commit is contained in:
King_DuckZ 2016-11-10 23:19:55 +01:00
parent 0b6cdeb5f5
commit 5e76b49cae
6 changed files with 64 additions and 11 deletions

View file

@ -0,0 +1,25 @@
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.
2) In SizeNotifiable's constructor RegisterBehaviour::Register(this) gets called. Being in the constructor, virtual calls on this are UB.
3) 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.
4) 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.