Description: polymorphism, virtual function, and parent subclass in C ++

Source: Internet
Author: User

This article mainly aims to explain the polymorphism in C ++, Which I encountered in learning Win32 and MFC programming.

 

First, let's look at a program:

 

# Include <iostream> <br/> using namespace STD; </P> <p> class cobject {</P> <p> public: <br/> virtual void serialize () {</P> <p> cout <"cobject: Serial ()/n "; <br/>}</P> <p >}; </P> <p> class cdocument: Public cobject {</P> <p> public: <br/> int m_data1; <br/> void func () {<br/> cout <"cdocument: func ()" <Endl; <br/> serialize (); <br/>}</P> <p> virtual void serialize () {<br/> cout <"cdocument: Serial () /n "; <br/>}< br/>}; </P> <p> class cmydoc: Public cdocument {</P> <p> public: <br/> int m_data2; <br/> virtual void serialize () {</P> <p> cout <"cmydoc: serialize ()/n "; </P> <p >}</P> <p >}; </P> <p> int main (void) {</P> <p> cmydoc mydoc; <br/> cmydoc * pmydoc = new cmydoc (); </P> <p> cout <"#1 testing" <Endl; <br/> mydoc. func (); </P> <p> cout <"#2 testing" <Endl; <br/> (cdocument *) (& mydoc )) -> func (); </P> <p> cout <"#3 testing" <Endl; <br/> pmydoc-> func (); </P> <p> cout <"#4 testing" <Endl; <br/> (cdocument) mydoc ). func (); </P> <p> return 0; <br/>}< br/>

 

The following inheritance relationships can be seen from the program:

Cmydoc-> cdocument-> cobject. Note that due to the existence of the inheritance relationship, the cmydoc class actually has the following member functions and variables:

1. func () inherited from cdocument

2. m_data1 is also inherited from cdocument.

However, both cmydoc and cdocument override the virtual function serialize () of their parent classes ().

 

Its running result is:

#1 Testing
Cdocument: func ()
Cmydoc: serialize ()

 

#2 testing
Cdocument: func ()
Cmydoc: serialize ()

 

#3 testing
Cdocument: func ()
Cmydoc: serialize ()

 

#4 testing
Cdocument: func ()
Cdocument: Serial ()

 

From the first three running results, we can conclude that mydoc is a cmydoc class, And pmydoc is a pointer to the cmydoc class, both of which are associated with the mydoc class, therefore, when you call serialize () in cdocument: func (), the serialize () of the parent class has been overwritten in the sub-class cmydoc (), therefore, it will eventually implement the call to the subclass cmydoc: serialize (), instead of executing the parent class cdocument: serialize ().

However, when executing the fourth test, the situation is different. Here, the cmydoc-type object upcast is forcibly converted to a cdocument-type object, which is a process of forcibly converting a subclass into a parent class, it is called object cutting.

Generally, from the perspective of memory usage, the subclass object is larger than the parent class object, because the subclass inherits the relevant member variables and member functions from the parent class, at the same time, it will add its own member variables and member functions in its own class. So here we use (cdocument) mydoc ). func (); When serialize () is called, cdocument: Serial () is called. In my understanding, the sub-class cmydoc: serialize () is used for upcast, this information is lost.

 

Well, let's talk about how virtual functions are implemented.

A virtual function is actually a dynamic binding mechanism, because during compilation, the compiler does not know whether to call the virtual function in the parent class or the virtual function in the subclass, it is dynamically determined during program execution. The essence of a virtual function is that the C ++ compiler uses a table to "indirectly" Call the function to be bound during execution (note the word "indirectly ). Such a table is called a virtual function table (vtable ). For each "class containing virtual functions", the compiler creates a virtual function table for it. Each element in the table points to the address of a virtual function. In addition, the compiler will certainly add a member variable to the category, which is a pointer (often called vptr) to the virtual function table ).

Every object derived from this class has such a vptr. When we call a virtual function through this object, we actually find the virtual function table through vptr, and then find the real address of the virtual function.

 

Now, we have at least a Supplementary Understanding of the implementation mechanism of the virtual function. What is the principle of the example program above?

The secret lies in this virtual function table and this indirect call method. The content of the virtual function table is based on the virtual function declaration order in the category and filled with the function pointers one by one. Derivative classes inherit the virtual function tables (and all other Members that can be inherited) of the basic class. When we rewrite the virtual function in the derivative class, the virtual function table will be affected: the function address referred to by the element in the table will not be the function address of the basic class, but the function address of the functions class.

 

That's why, in the first three tests, when the cdocument: func () function calls serilize (), it calls the serilize () virtual function rewritten by the quilt-class cmydoc.

 

In specific program design, how should we use the nature of virtual functions to achieve unified interfaces?

The method is as follows:

Declare a virtual function in the base class (it is best to declare it as a pure virtual function, so that the base class becomes an abstract base class) without declaring its method body, let all subclasses that inherit from the base class override this virtual function. In the future, if you want to call the interface functions of these subclasses in a unified way, you only need to obtain the pointer of the abstract base class, then obtain the address of each subclass object, and assign the pointer to the base class, finally, the interface function is called through the base class pointer.

 

Let me give an example. This is clear:

# Include <iostream> <br/> # include <fstream> <br/> using namespace STD; </P> <p> ofstream out ("out.txt "); </P> <p> class cshape {</P> <p> Public: <br/> virtual void display () = 0; <br/> }; </P> <p> class ccircle: Public cshape {</P> <p> Public: <br/> virtual void display () {<br/> out <"display Circle/n"; <br/>}< br/>}; </P> <p> class crectangle: public cshape {</P> <p> Public: <br/> virtual void display () {<br/> out <"display rectangle/n "; <br/>}</P> <p >}; </P> <p> class cstar: Public cshape {</P> <p> public: <br/> virtual void display () {<br/> out <"display Star/n"; <br/>}< br/> }; </P> <p> int main (void) {</P> <p> cshape * array [] = {<br/> New crectangle (), <br/> New ccircle (), <br/> New cstar () <br/>}; <br/> int arraysize = sizeof (array) /sizeof (* array [0]); // 3 <br/> cout <arraysize <Endl; </P> <p> for (INT I = 0; I <arraysize; I ++) <br/> array [I]-> display (); </P> <p> return 0; <br/>}< br/>

 

The running result is as follows. The focus is on the array in the main function.

 

Display rectangle

 

Display Circle

 

Display Star

 

Okay. Next, let's talk about a few small summaries of virtual functions. These are all from the simple introduction to MFC.

1. If you want the derivative category to redefine a member function, you should set this function to virtual in the basic category.

2. Call different functions with a single command. This property is called polymorphism, which means "the ability to assume your forms", that is, polymorphism.

3. Since the virtual function in the abstract class is not intended to be called, we should not define it. We should set it as a pure virtual function (Add "= 0" after the function declaration)

4. abstract classes cannot generate object entities, but we can have pointers to abstract classes to facilitate the operation of various derivative classes in abstract classes.
Virtual functions are still derived from virtual functions, and virtual keywords can be omitted.

 

 

Well, let's look at another example. This example is also about the parent class and subclass:

 

# Include <iostream> <br/> # include <fstream> <br/> using namespace STD; </P> <p> ofstream out ("out.txt "); </P> <p> class cshape {</P> <p> Public: <br/> void display (); <br/> void outputname () {<br/> out <"shape/n"; <br/>}< br/>}; </P> <p> class ccircle: public cshape {</P> <p> Public: <br/> void display () {<br/> out <"display Circle/n "; <br/>}< br/> void outputname () {<br/> out <"circle/n"; <br/>}< br/> void el Lo () {</P> <p> out <"Hello! /N "; <br/>}< br/>}; </P> <p> int main (void) {</P> <p> cshape * shape; <br/> ccircle circle; </P> <p> shape = & circle; <br/> shape-> outputname (); <br/> // shape-> Hello (); </P> <p> return 0; <br/>}</P> <p>

 

The running result is as follows:

 

Shape

From such a small program, we can see that if the ccircle object address is assigned to its parent class cshape pointer, then this pointer can only call some member functions in the parent class cshape, you cannot call member functions in the ccircle subclass. So we can draw the following conclusions:

1. If you use a "Basic Category Pointer" to point to "derivative category object", you can only call functions defined by the Basic category.

2. If you direct a derivative category pointer to a basic category object, you must first make an explicit transformation action (explicit cast ). This practice is dangerous and does not conform to the real life experience. It will also bring confusions to programmers in programming.

3. If the basic category and derivative category define "member functions with the same name", which function is called when the member function is called through the object pointer, the pointer must be based on the original type of the pointer, rather than the type of the object actually referred to by the pointer.

 

To sum up, we have a deeper understanding of the polymorphism and virtual function mechanisms in C ++ and the results of pointer transformations such as parent classes.

 

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.