[英]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? 要回答实施方面的问题,为什么会看到这种行为?
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. 该参数未取消引用(因为未使用任何成员变量),因此调用成功。
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.