The general rule of thumb is not to call a virtual function from a constructor because it can lead to unpredictable behavior. So why does it work sometimes?
I recently wrote a couple of base classes with pure virtual functions, and accidentally included an indirect call to those functions in the constructor. I realized my mistake and corrected it, but one of them worked while the other did not.
Here's the definition of the class that worked:
template <typename TWindow>
class dialog_base
{
static INT_PTR CALLBACK dlg_proc_internal
(HWND,
UINT,
WPARAM,
LPARAM);
protected:
dialog_base
(const LPCWSTR templateName,
const HWND parent)
{
CREATESTRUCT create;
create.lpCreateParams = this;
m_hwnd = CreateDialogParam(
hinstance_, templateName, parent, dlg_proc_internal,
reinterpret_cast<LPARAM>(&create));
}
HWND m_hwnd;
virtual INT_PTR CALLBACK dlg_proc
(UINT,
WPARAM,
LPARAM) = 0;
public:
virtual ~dialog_base()
{
DestroyWindow(m_hwnd);
}
HWND GetHandle() const;
void show() const;
};
In this class, the DialogBoxParam function calls dlg_proc_internal
, passing the WM_NCCREATE
message:
template <typename TWindow>
INT_PTR dialog_base<TWindow>::dlg_proc_internal
(HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
dialog_base<TWindow>* pThis;
if (msg == WM_NCCREATE)
{
pThis = static_cast<dialog_base<TWindow>*>(reinterpret_cast<
CREATESTRUCT*>(lParam)->lpCreateParams);
SetLastError(0);
if (!SetWindowLongPtr(
hWnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(pThis)) && GetLastError() != 0)
return 0;
}
else
{
pThis = reinterpret_cast<dialog_base<TWindow>*>(
GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
return pThis
? pThis->dlg_proc(msg, wParam, lParam)
: DefWindowProc(hWnd, msg, wParam, lParam);
}
This function retrieves the pointer passed as the last argument to CreateDialogParam
and stores it in the window so that it can be retrieved again in later calls to the function.
It then mistakenly calls the pure virtual function dlg_proc
instead of returning -- and appears to work just fine through the constructor of a child class.
I created a different class which was almost identical, except that it called CreateWindowEx instead of CreateDialogParam
. The pointer argument was passed in much the same way, and used to call the pure virtual function. This time, it failed as one might expect. So what's the difference between the two situations?
EDIT:
Perhaps I should clarify. I'm not asking "Why can't I call virtual members from a constructor?". I'm asking about why the process of resolving virtual members before the object is constructed can sometimes create situations in which an error does not occur, and the correct function is called.