Method for private member variables of the metadata class

Source: Internet
Author: User
Although there is no practical significance, it is helpful to understand the object model of C ++.

A few days ago, I saw a post in the forum with the following content:
(See original post: http://community.csdn.net/Expert/topic/5014/5014384.xml? Temp =. 3018152)
==========================================================
Class
{
PRIVATE:
Int K;
};
It is required that you do not need to use a friend yuan. Do not add any code in this class to access the member variable k.
A complete source code can be posted for testing.
==========================================================

The problem to be solved does not have much practical significance, but if you try to solve it, and it is more complex than it, I think it is helpful to understand at least the object model of C ++.

Before starting the discussion, I have to say that this question has a major logical defect as a question: "Do not add a line of code to the class ", I really don't know how to validate the correctness of the solution provided by the problem-solving person. There is only one private address. Do I use the read-out solution provided by the problem-solving person to verify the write solution provided by him? You read it in your way and tell me that it is the value you write in your method-is that convincing? So I decided to change the problem and make it a little specific as follows:
Class test {
Public:
Int get_value () {return value ;}
PRIVATE:
Int value;
};
It is required that you do not need to use any code in this class. Change the value of member variable k to 100. The result is naturally verified by the Public member function get_value.

"Do not add any code in the class",
# Define private public
I really can't think of other "tricks. Let's just think about it. Several friends in the Forum provide the following code (for Space Control, all Program sections in this article assume that the <iostream> header file is included and the STD namespace is introduced. If necessary, there are other header files ):
Test T;
* (Int *) & t = 100;
Cout <t. get_value () <Endl;
This method utilizes the features of the Object Memory layout: the entire class has only one integer member, no inheritance or virtual inheritance, and no virtual functions, the address of this object is the address of its first member variable. Therefore, you only need to convert the object address to an integer type, and the obtained address is the address of the member variable, then, unreference the converted address and modify it. In vc2003, verify that the result is correct.
However, the forced conversion of pointers is always uncomfortable and insecure. The above most critical statement is equivalent:
* Reinterpret_cast <int *> (& T) = 100;
That is to say, it uses the "Strongest" pointer Conversion Method in the C ++ language (that is, it is the strongest, because no pointer can be converted between them ). In fact, we can do a bit more "civilized" by defining another consortium, for example:
Union testint {
Test T;
Int I;
};
Then:
Testint Ti;
Ti. I = 100;
Cout <ti. T. get_value () <Endl;
But basically, the mechanism is consistent with the above pointer conversion.

This method has no major problem, but has limitations. It can only be used to modify the first member of the class. If you add a member before the value, for example:
Class test {
Public:
Int get_value () {return value ;}
PRIVATE:
Char ch;
Int value;
};
This method does not work.
Of course, you can manually calculate and think that char occupies a byte, so you will try to get the object address and Add 1 to get the member value address. However, first, this method cannot be implemented across platforms. The length of char and INT types in different platforms and compiler implementations may be different. Second, the word alignment issue is not considered, in the memory, the value member is generally not arranged after ch, but several bytes are opened between them, and the int type is aligned to another location, such as the address multiple of 4; what's worse, word alignment is not only related to the platform, but also to precompiled commands and even compilation options. Therefore, we should give up this manual calculation method.
Some people mentioned that using a macro to find the offset between the value member and the starting address of the entire object, that is, defining a macro:
# Define offset (type, Mem) (INT) (char *) & (type *) 0)-> MEm ))
This macro converts the 0 address to the type pointer type and then "retrieves" mem members from this pointer, after the address translation of the mem member, the result is the offset of the mem member to the entire object (since we start from 0, we do not need to subtract the starting address 0 ).
Then, we use this macro to act on the original class and target fields, namely:
Offset (test, value)
You can get the offset of the Value Field in the test Type object. With this offset added to the first address of the object, you can get the address of the value variable, so that it can be referenced and modified as above.
This method not only looks uncomfortable, but is hard to understand. In fact, it doesn't work either, because the technique used by this macro is to access the value member from the pointer of the test Type -- and valuee is private! Therefore, compilation fails.

A friend in the Forum proposed another method to skillfully deal with this complicated class. First, we made a helper class, which is similar to the test class, the only difference is that its members are all public:
Class testtwin {
Public:
Int get_value () {return value ;}
Public:
Char ch;
Int value;
};
Therefore, the testtwin class is no different from the original test class in the memory layout. Through pointer conversion, it is easy to use it to modify the value member of the test class object:
Test T;
Testtwin * P = reinterpret_cast <testtwin *> (& T );
P-& gt; value = 100;
Cout <t. get_value () <Endl;
If you are not familiar with the explicit pointer Conversion Method of C ++: reinterpret_cast, you can consider it to be equivalent to the C-style:
Testtwin * P = (testtwin *) & T;
The preceding two statements can be combined and written directly as follows:
Reinterpret_cast <testtwin *> (& T)-> value = 100;
Also, friends who hate pointer operations can still use the consortium method described earlier to use this mold class, but the consortium defined this time is as follows:
Union testtesttwin {
Test T;
Testtwin TW;
};
The program is like this:
Testtesttwin ttw;
Ttw.tw. value = 100;
Cout <ttw. T. get_value () <Endl;

Have all the problems been solved? If the class is more complex, will there be limitations? Let's change the class:
Class test {
Public:
Int get_value () {return value ;}
~ Test (){}
PRIVATE:
Char ch;
Int value;
Public:
Int;
Double B;
Protected:
String E;
PRIVATE:
Short D;
};
This time not only has many more members, but also has members of the string type (which must include <string> ), create a virtual destructor (we all know that classes with virtual functions will lead to one more virtual table pointer in their instances ). However, we will see later that the virtual function has little impact on the problem we are discussing. We just want to prove that as long as the method is good enough, we are not afraid of more complex objects.
Where is the above mold solution? Why can't I create another class, change the value to public, and then use it to "hide" the value member in the original object?
The reason is that the C ++ language only ensures the same access section in the class (that is, the part between an access permission modifier public/private/protected and another modifier) non-static member variables defined in will be distributed in the memory according to the declared sequence, but it is not guaranteed that all member variables that span different access sections will be stored in the declared sequence in the memory, A compiler may combine all the private blocks and even throw them to the back of all the protected members (although VC didn't do this ).
In other words, changing the access permission of a Member may change the memory layout of the object. As a result, the changed model will no longer be able to hold members in the corresponding position.
But there are still some ways to do this. You just need to make some improvements:
In the existing C ++ object model, adding a non-virtual member function for the class will not change the memory layout of the object. We can use this to write a testtwin:
Class testtwin {
Public:
Int get_value () {return value ;}
Void set_value (INT v) {value = V ;}
~ Testtwin (){}
PRIVATE:
Char ch;
Int value;
Public:
Int;
Double B;
Protected:
Float E;
PRIVATE:
Short D;
};
This mold is only different from the original test class: a public, non-virtual set_value method is added to assign values to private members. Therefore, the program can be written as follows:
Test T;
Reinterpret_cast <testtwin *> (& T)-& gt; set_value (100 );
Cout <t. get_value () <Endl;
Verification passed.
The added virtual function is just a blind object, and it has almost no connection with the method we adopt, so there is no need to worry about the impact of virtual functions on memory distribution, which will affect the correctness of this method. But when it was done, the method using consortium really didn't work this time, because classes with destructor can no longer be placed in the consortium -- otherwise, when the lifecycle of the consortium instance ends, who will the consortium describe?

After thinking about shutting down the sky, we can only think of so much.
Finally, I cannot admit it. The phrase "adding a non-virtual member function will not change the memory layout of the object" cannot be directly supported by the C ++ standard, this is okay for most compilers. Because "each instance of the class has an independent member variable, all instances of the class share a member function. "The C ++ object model was proposed by Mr. Bjarne stroustrup, the father of C ++, its time and space efficiency are well in line with the original design intention of the C ++ language. Not only does the modern c ++ compiler not do this, even the Java/C # compiler does the same. Therefore, it is also a "relative truth.

Original article address:Http://community.csdn.net/Expert/topic/5039/5039857.xml? Temp =. 728924.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.