问题
Lets say we have a class
class A
{
int x;
public:
void sayHi()
{
cout<<"Hi";
}
};
int main()
{
A *a=NULL;
a->sayHi();
}
The above code will compile on Turbo C (where I tested) and print Hi
as output.
I was expecting crash because a
is NULL
. More over if I make sayHi()
function virtual, it says
Abnormal temination(Segmentation fault in gcc)
I know a lot of it is implementation dependent but if anybody could throw some light on any implementation or just give an overview it would be really nice.
回答1:
Obviously, the code has undefined behavior, i.e., whatever you get is by chance. That said, the system doesn't need to know about the object when calling a non-virtual member function: It can just be called based on the signature. Further, if a member function doesn't need to access a member, it doesn't need really need an object at all and can just run. This is what you observed when the code printed some output. Whether this is how the system is implemented isn't defined, however, i.e., nothing says it works.
When calling a virtual function type system starts off looking at a type information record associated with the object. When calling a virtual function on a NULL
pointer, no such information exists and attempting to access it probably leads to some sort of crash. Still, it doesn't have to but it does for most system.
BTW, main()
always returns int
.
回答2:
In C++, the methods of a class are not stored inside the instances of that class. They're simply some "special" functions that transparently accept the this
pointer in addition to the arguments specified by the programmer.
In your case, the sayHi()
method does not reference any of the class fields, therefore, the this
pointer (which is NULL
) is never followed.
Make no mistake, though, this is still undefined behavior. Your program may choose to send nasty emails to your contact list when you invoke this. In this particular instance, it does the worst thing, and appears to work.
The virtual
method case has been added since I answered the question, but I won't refine my answer, since it's included by others' answers.
回答3:
As a generalisation, the layout of a object instantiated from a class with no super classes and virtual functions is as follows:
* - v_ptr ---> * pTypeInfo
| |- pVirtualFuncA
| |- pVirtualFuncB
|- MemberVariableA
|- MemberVariableB
v_ptr
is a pointer to the v-table - which contains addresses of virtual functions and RTTI data for the object. Classes without virtual functions don't have v-tables.
In your example above, class A
has no virtual methods and thus no v-table. This means the implementation of sayHi()
to call can be determined at compile time and is invariant.
The compiler generates code that sets the implicit this
pointer to a
and then jumps to the beginning of sayHi()
. Since the implementation has no need for the object's contents, the fact that it works when the pointer is NULL
is a happy coincidence.
If you were to make sayHi()
virtual, the compiler cannot determine the implementation to call at compiler time, so instead generates code that looks up the address of the function in the v-table and calls it. In your example where a
is NULL
, the compiler reads the contents of address 0
, causing the abort.
回答4:
If you call a non-virtual method of a class, for the compiler it is enough to know what class the function belongs to and by dereferencing - albeit a NULL - pointer to a class to call the method, the compiler gets that information. The sayHi() method is pretty much just a function that takes pointer to the class instance as a hidden parameter. This pointer is NULL but that doesn't matter if you do not reference any attributes in the method.
The moment you make this method virtual, the situation changes. The compiler does not know what code is associated with the method at compile time any more and must figure that out at the runtime. What it does is that it looks into a table that basically contains function pointers for all virtual methods; this table is associated with the class instance so it looks at piece of the memory relative to the NULL pointer and therefore crashes in this case.
来源:https://stackoverflow.com/questions/12660829/how-does-c-store-functions-and-objects-in-memory