When can I access an object member directly in memory? No getters called
It is usually allowed to do something like that (no comments on the code please :-))
class SimpleClass {
int member;
};
SimpleClass instance;
int* pointer = (int*) &instance;
However, if I define my class as:
class SimpleClass {
virtual void foo();
int member;
};
I can't do that anymore. Well, I guess I can, of course; it's just more complex.
So I was wondering: is it s开发者_如何学Goomewhere specified, or the only way to know when I can do that is just to use some common sense? Not counting alignment issues, which can usually be solved
Generally you want to keep the innards of a class of closed off from the outside world as you can, but if you do need to access a member directly simply specify it as public and take a pointer directly to it.
class SimpleClass {
public:
int member;
};
SimpleClass instance;
int* pointer = &instance.member;
I would avoid accessing the members in the way you describe because as you noted small changes in the class can mess it up, which may be fine whilst you are writing the code but when you come back to it much later you will probably overlook such subtleties. Also unless the class is constructed entirely of native data types, I believe the compiler's implementation will affect the required offset as well.
You can only do this safely if your class is plain old data (POD). From Wikipedia:
A POD type in C++ is defined as either a scalar type or a POD class. A POD class has no user-defined copy assignment operator, no user-defined destructor, and no non-static data members that are not themselves PODs. Moreover, a POD class must be an aggregate, meaning it has no user-declared constructors, no private nor protected non-static data, no base classes and no virtual functions. The standard includes statements about how PODs must behave in C++.
See this page for many details on POD types. In particular,
"A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member ... and vice versa" [§9.2, ¶17].
class SimpleClass {
int member;
};
SimpleClass instance;
int* pointer = (int*) &SimpleClass;
In the above code pointer
points to SimpleClass::member
and hence you can access SimpleClass::member
in this way.
Once you add an virtual
method to the class SimpleClass, the memory map of the object changes.
class SimpleClass {
virtual void foo();
int member;
};
Every object of SimpleClass
now contains a special pointer called as vptr
which points to a vtable
which is a table of addresses of all virtual functions in SimpleClass
. The first 4 bytes of every object of SimpleClass
now point to the vptr
and not SimpleClass::member
and that is the reason you cannot access member
in the same way as first case.
Ofcourse, virtual behavior is implementation detail of compilers and vptr
nor vtable
are specified in the standard but the way i mentioned is how most compilers would implement it.
Since the implementation detail might be different for each compiler you should rely on accessing class members through pointers to class(especially polymorphic classes). Also, doing so defeats the entire purpose of Abstraction
through Access Specifiers
.
All right, several things.
Your first code example won't compile because you can't have a pointer to a class. I think you meant this:
SimpleClass instance;
int* pointer = (int*) &instance;
(Please don't code you haven't tried compiling.)
This kind of casting is powerful and dangerous. You could just as well cast to a pointer to some type (say, char*
) and as long as it was a type no larger than int, the code would compile and run. This kind of reinterpretation is like a fire axe, to be used only when you have no better choice.
As it is, pointer
points to the beginning of 'instance', and instance
begins with instance.member
(like every instance of SimpleClass
), so you really can manipulate that member with it. As soon as you add another field to SimpleClass
before member
, you mess up this scheme.
In your second code example, you can still use pointer to store and retrieve an int
, but you're doing so across boundaries within the memory structure of instance
, which will damage instance
beyond repair. I can't think of a good metaphor for how bad this is.
In answer to your questions: yes, this is specified somewhere, and no, common sense isn't good enough, you'll need insight into how the language works (which comes from playing around with questions like this).
精彩评论