C++ :: Calling Member function via pointer before constructor exit

Viewed 278

I am in a situation where I share pointer to member function to outside during constructor execution. and the member function can be called any time, even before the constructor get completed. I am assuming it could lead to undefined behavior. this situation arise in following condition.

ButtonCtrl::ButtonCtrl(int id, HWND hwnd_parent, int x, int y,
                       int w, int h, const string& text) 
{
    this->hwnd = CreateWindowExW(0, L"BUTTON", this->wtext.c_str(),
                        WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                        x, y, w, h, hwnd_parent, (HMENU)id, 
                        GetModuleHandle(NULL), NULL);

    this->wndproc_org = (WNDPROC) SetWindowLongW(this->hwnd, GWL_WNDPROC, 
                                      (LONG) &ButtonCtrl :: wndproc_new);
}

Here wndproc_new is static class method but consider it none static. Now in SetWindowLongW method I replace Button's window procedure with my window procedure (wndproc_new ). As button already created using CreateWindowExW it's message loop may already started. and Now it can call my wndproc_new any time. even before this constructor got finish. So my question is, Do I get undefined behavior or is it valid ?

2 Answers

formal, even from language view, you can call member functions and even virtual functions from constructor body. because virtual table for your class (if it have virtual functions) will be initialized before constructor body. another question - are you initialize all your instance at this point already. i be not call this from constructor, not because this is wrong, UB etc (no), but by style reason.

Now it can call my wndproc_new any time. even before this constructor got finish

this is wrong. wndproc_new is not interrupt. it can not be called at any time. this callback can be called by system only when you call some concrete api like GetMessage , PeekMessageW, SendMessage, CreateWindowEx, etc) which can call your window procedure callback. your wndproc_new will be called only inside this api call, but not from random place.

also as side note need use SetWindowLongPtrW and GWLP_WNDPROC even for 32 bit code. and i strong doubt that need change default WC_BUTTON window procedure. however if really need subclass it - better use SetWindowSubclass or methods like AtlThunk_*


the general rule - set callback only when you full ready for incoming calls. at any time (despite in concrete case wndproc_new can be called only inside several api calls and only from your current thread). anyway after constructor body begin executed - possible call any member functions of class

I really appreciate the answer and all the comment. But I did not find what I am looking for. Probably I fail to explain my question. But the first comment by Evg is the simple and straightforward answer.

You can call member functions before a constructor finishes, provided you don't read uninitialized members in those functions. – Evg

I search and read some documentation and test, debug some code. I found something which I would like to share here and which may elaborate the answer. The answer may seem off-track but eventually we get on-track.

When the code compiled the member function compiled like other ordinary function. but as we know with special first argument the this pointer.

Can we get pointer to member function without object ?

yes, In the following code we get the pointer to member function without any object (Don't complain about cast, compiler will do). this indicates that member function does not belongs to object (Half True Sentence). But, it is this pointer that binds member function to object. it is kind of C struct with function pointer variable where first argument of the function is pointer to C struct itself.

#include <iostream>
using namespace std;

class A
{
    public:
        int test_fun()
        {
            cout << "in test_fun" << endl;
            return 42;
        }
};

typedef int (*INT_FUN_PTR)();
int main()
{
    INT_FUN_PTR test_fun_ptr = (INT_FUN_PTR) &A::test_fun;
    int i = test_fun_ptr();
    return 0;
}

But as we cast member function pointer to normal function pointer we get compiler warning. let's ignore it.

can we call this member function like other normal function using pointer ?

the answer is yes and no. We can but we should never call it like other normal function. As the above code compiled it run normally but if we debug and look at test_fun function's stack we can see that the this pointer included in stack and it contains garbage value because we did not call test_fun using any object notation. we must not use the this pointer.

How the this pointer get into stack ?

May be because of function's calling convention (and/or compiler). According to microsoft's documentation, __thiscall is the calling convention for member function. and the this pointer being passed via register ECX, and not on the stack, on the x86 architecture.

what if we define test_fun with some arguments ?

Following is the redefine version of test_fun with int argument.

#include <iostream>
using namespace std;

class A
{
    public:
        int test_fun(int i)
        {
            cout << "in test_fun" << endl;
            return i;
        }
};

typedef int (*INT_FUN_PTR)(int);
int main()
{
    int i = 0;
    int* p = &i;
    INT_FUN_PTR test_fun_ptr = (INT_FUN_PTR) &A::test_fun;
    *p = test_fun_ptr(42);
    //test_fun_ptr(42); // program crash
    return 0;
}

Now if we debug this code we can see the stack of the main function after calling test_fun get cluttered. if we call it second time program get crash. if we allocate memory and use it after first time calling test_fun again program crash. because of the calling convention and because we call test_fun as normal function.

Now lets try to change the calling convention of test_fun using __cdecl

#include <iostream>
using namespace std;

class A
{
    public:
        int __cdecl test_fun(int i)
        {
            // here stack get cluttered
            cout << "in test_fun" << endl;
            return i;
        }
};

typedef int (__cdecl *INT_FUN_PTR)(int);
int main()
{
    int i = 0;
    int* p = &i;
    INT_FUN_PTR test_fun_ptr = (INT_FUN_PTR) &A::test_fun;
    *p = test_fun_ptr(42);
    test_fun_ptr(42); // program will not crash
    return 0;
}

This time the test_fun function's stack get cluttered. I am surprise, is it possible to use member function as normal function by specifying any of calling convention ? but it is not the part of this discussion.

As we can see the member function is just like normal function with special calling convention. it is always there. it bind to object by the this pointer. when the object get created, memory allocated (on stack or heap) for members and pointer to memory get push to register. the constructor is the first function which get called. and we can access the this pointer via constructor for the first time. if we are able to get the this pointer before constructor call (may be via malloc which will not call constructor at all), we can call member function even before constructor call. provided not using uninitialized members or even we can initialize members there.

In conclusion, yes it is perfectly normal to call member function before a constructor finishes as many time as you want provided not using uninitialized members.

Related