简体   繁体   English

C ++指针可以在没有对象的情况下调用成员函数

[英]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. 令人惊讶的是,人们可能将其称为功能,但是我经常说它是C ++的另一个错误,我们可以通过指针调用成员函数而无需分配任何对象。 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 输出: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. 取消引用NULL指针是未定义的行为。

BTW, there is no thing such as "bug of C++". 顺便说一句,没有诸如“ C ++的bug”之类的东西。 The bugs may occur in C++ Compilers not in the language it self. 在C ++编译器中,错误可能不是以其自身的语言出现的。

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. 非虚拟调用仅作为普通函数调用实现,并且this指针(值null )作为参数传入。 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. 虚拟调用需要在vtable进行查找,以获取要调用的实际函数的地址。 The vtable address is stored in a pointer in the data of the object itself. vtable地址存储在对象本身数据中的指针中。 Thus to read it, a de-reference of the this pointer is required - segmentation fault. 因此要读取它,必须取消引用this指针-分段错误。

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 . 当您执行p->MemberFunction() ,编译器仅p->MemberFunction() p并尝试使用p的类型信息(即Class A查找函数MemberFunction

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. 如果函数具有对某些类变量的引用,则在访问它们时,您可能会遇到“ Segmentation Fault因为没有对象,但是由于并非如此,因此该函数可以正确执行。

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. 一些编译器可以选择在访问成员函数之前查看对象的指针是否为null,但是指针可能具有一些垃圾值而不是0 ,这是编译器无法检查的,因此通常编译器会忽略此检查。

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: 您甚至可以调用一个仅接受1个参数的函数,然后接收第二个参数,如下所示:

#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. 在Windows下编译并运行,第二个参数的结果为“ arg”。 This is not a fault within C++, it is just plain stupid on my part :) 这不是C ++的错,对我而言只是愚蠢的:)

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. 其中&obj成为foo方法的this指针。 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. 由目标函数检查是否将null传递给它。 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. 只要f2非虚,如果f2不读/写东西出来的this ,包括任何虚函数调用。 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. 后期绑定完全基于this指针所指向的内容。 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. 将涉及通过虚函数表查找来查找obj的当前类型。 therefore, obj must not be null. 因此, obj不能为null。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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