So far, we have a good macro-encapsulated code style, a basic class object structure simulation code, but never touched the core implementation of OO. This time, we finally need to enter the actual content-Object Inheritance and interface implementation.
Memory layout of a single object
In "the syntax of Crest-macro magic show", we implemented a simple object that contains member variables and member functions, as shown in. Buffer points to the character buffer, and the Format of the virtual function is a function pointer.
This structure places data and member functions in the instance object together, but this structure wastes a lot of space once it encounters a large-scale object use scenario, because for the same object, the member functions are the same, and there is no need to keep a copy of the member functions for each object. So we can modify this structure:
This is simple. A CString class can have many instances in the memory, but all instances share the same CStringType object. The CStringType object is responsible for storing pointers of virtual and pure virtual functions. The so-called CStringType is called VTable in C ++ and VMT in delphi. In fact, this exists in all OO languages.
If you are more careful, you may have some questions. Read the code in the previous article. Format is a virtual function, OnFormat is a pure virtual function, and DoFormat is a real function, real functions should not appear in CStringType. Yes, because we use the C language struct to simulate the class. Originally, struct does not support visibility definitions such as public, protected, and private. The simulation can only be public, which is one of the restrictions. If we need to strictly distinguish between functions with virtual and real types, it will also lead to inconsistency in the syntax of calling functions with virtual and real types due to the limitations of the C language, which leads to a lot of trouble. Moreover, java does not distinguish between virtual and real functions, all of its member functions are virtual functions and can be reloaded. Therefore, you can refer to the java features.
Object Inheritance Structure
We have completed the design of memory layout for a single object. Next we will implement class inheritance.
The first requirement for implementing the inheritance structure is the conversion between the base class and the derived class. In programming practice, we know that a derived class can be used as a base class between a base class and a derived class, just like CObject obj = new CString, in this way, CString str = (CString) obj can be forcibly converted. Most classes with a single inheritance structure are implemented using a simple method to ensure that the first half of the CString and the memory structure of the Base Class CObject are identical.
As shown in, if a pointer points to the CString structure, because the memory structure is the same, it is equivalent to pointing to a CObject structure.
There is also another conversion method between the base class and the derived class, that is, the QueryInterface method of COM. All type conversions are obtained by calling QueryInterface, such a base class, derived class, or even an interface, it can even be implemented using different objects, but it is costly and is not needed for the time being.
For a CString class, we need to use CString and CStringType together to complete the function of the entire class. The base class CObject also needs CObject and CObjectType. Therefore, the homogeneous principle of the base class and the derived class must also apply between CStringType and CObjectType. In this way, we can see that once CString fills in the toString position of CStringType with its own toString function pointer, we can implement the so-called "override )".
Note that the this pointer we mentioned earlier is implicitly divided into two parts: CString and CStringType.
But what if we want to call the function of the base class? C ++ syntax is CObject: Format (xxx), java syntax is super. Format (xxx), C # syntax is similar to java. The key here is whether to set a pointer to CObjectType in CStringType. From the usability perspective, the java/C # method is simpler, and the C ++ method is suspected of duplication of information. In case the base class name is modified, C ++ needs to modify more than one place, while java/C # only needs to modify one place. However, if we use the C ++ method to implement the Crest, we do not need to place a pointer in CStringType. Otherwise, we need. This may be more important in MCU programming. The structure changes to the following:
Interface implementation
A basic single inheritance structure has been designed. Next, you need to implement the interface ). Compared with the base class, the interface is much simpler. Because the interface does not involve data, all the work is focused on how to adjust CStringType.
Before designing an interface, let's talk about the theory. If all the member functions of a class CString are considered as a set a, all the function sets in interface B implemented by CString are the subset of. Therefore, the implementation of the interface is simply to copy the member functions of the Host Object and rearrange them in a specified order. Another difference between interfaces and classes is that interfaces do not need to be homogeneous with the Host object, and the only difference between the same interfaces implemented by different types is the order of functions.
Specifically, we use an additional table to store it, which we call InterfaceChain.
This structure can support the IFormat fmt = (IFormat) new CString () statement, but what about CString str = (CString) fmt? Error! Therefore, we need to add an owner for each InterfaceChain element to point them to CStringType, so that we can use CString str = (CString) fmt. owner to convert the interface instance pointer back to the object pointer.
The above design is our core structure, and will become more and more complex with the increase of future features. Such a structure cannot be displayed to users. On the contrary, the deeper the structure, the better it is. The magic macro is needed. Next, let's talk about how to encapsulate and reference these structures.