简体   繁体   中英

C++ Pointer can call Member Function without Object

Amazingly people may call it feature but I use to say it another bug of C++ that we can call member function through pointer without assigning any object. See following example:

class A{    
public:   
       virtual void f1(){cout<<"f1\n";}
       void f2(){cout<<"f2\n";};
};

int main(){    
    A *p=0;
    p->f2();
    return 0;
}

Output: f2

We have checked this in different compilers & platforms but result is same, however if we call virtual function through pointer without object then there occur run-time error. Here reason is obvious for virtual function when object is checked it is not found so there comes error.

This is not a bug. You triggered an Undefined Behavior. You may get anything including the result you expected.

Dereferencing a NULL pointer is undefined behavior.

BTW, there is no thing such as "bug of C++". The bugs may occur in C++ Compilers not in the language it self.

As pointed out, this is undefined behaviour, so anything goes.

To answer the question in terms of the implementation, why do you see this behaviour?

  1. The non-virtual call is implemented as just an ordinary function call, with the this pointer (value null ) passed in as paremeter. The parameter is not dereferenced (as no member variables are used), so the call succeeds.

  2. The virtual call requires a lookup in the vtable to get the adress of the actual function to call. The vtable address is stored in a pointer in the data of the object itself. Thus to read it, a de-reference of the this pointer is required - segmentation fault.

When you create a class by

class A{    
public:   
       virtual void f1(){cout<<"f1\n";}
       void f2(){cout<<"f2\n";};
};

The Compiler puts the code of member functions in the text area. When you do p->MemberFunction() then the compiler just deferences p and tries to find the function MemberFunction using the type information of p which is Class A .

Now since the function's code exists in the text area so it is called. If the function had references to some class variables then while accessing them, you might have gotten a Segmentation Fault as there is no object, but since that is not the case, hence the function executes properly.

NOTE : It all depends on how a compiler implements member function access. Some compiler may choose to see if the pointer of object is null before accessing the member function, but then the pointer may have some garbage value instead of 0 which a compiler cannot check, so generally compilers ignore this check.

You can achieve a lot with undefined behavior. You can even call a function which only takes 1 argument and receive the second one like this:

#include <iostream>

void Func(int x)
{
    uintptr_t ptr = reinterpret_cast<uintptr_t>(&x) + sizeof(x);
    uintptr_t* sPtr = (uintptr_t*)ptr;
    const char* secondArgument = (const char*)*sPtr;
    std::cout << secondArgument << std::endl;
}

int main()
{
    typedef void(*PROCADDR)(int, const char*);
    PROCADDR ext_addr = reinterpret_cast<PROCADDR>(&Func);

    //call the function
    ext_addr(10, "arg");
    return 0;
}

Compile and run under windows and you will get "arg" as result for the second argument. This is not a fault within C++, it is just plain stupid on my part :)

This will work on most compilers. When you make a call to a method (non virtual), the compiler translates:

obj.foo();

to something:

foo(&obj);

Where &obj becomes the this pointer for foo method. When you use a pointer:

Obj *pObj = NULL;
pObj->foo();

For the compiler it is nothing but:

foo(pObj);

ie:

foo(NULL);

Calling any function with null pointer is not a crime, the null pointer (ie pointer having null value) will be pushed to call stack. It is up to the target function to check if null was passed to it. It is like calling:

strlen(NULL);

Which will compile, and also run, if it is handled:

 size_t strlen(const char* ptr) {

     if (ptr==NULL) return 0;
     ...  // rest of code if `ptr` is not null
}

Thus, this is very much valid:

((A*)NULL)->f2();

As long as f2 is non-virtual, and if f2 doesn't read/write anything out of this , including any virtual function calls. Static data and function access will still be okay.

However , if method is virtual, the function call is not as simple as it appears. Compiler puts some additional code to perform late binding of given function. The late binding is totally based on what is being pointed by this pointer. It is compiler dependent, but a call like:

obj->virtual_fun();

Will involve looking up the current type of obj by virtual function table lookup. therefore, obj must not be null.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM