Virtual functions and polymorphism (C ++)

Source: Internet
Author: User

Virtual functions are used in C ++ to implement polymorphism. The core concept is to access the functions defined by the derived class through the base class. Suppose we have the following class layers:
Class
{
Public:
   Virtual void Foo () {cout <"A: Foo () is called" <Endl ;}
};
Class B: public
{
Public:
   Virtual void Foo () {cout <"B: Foo () is called" <Endl ;}
};

In this case, we can:
A * A = new B ();
A-> Foo (); // here, although a points to a, the called function (FOO) is B!

This example is a typical application of virtual functions. Through this example, you may have some concepts about virtual functions. It is virtual on the so-called "Deferred Association" or "Dynamic Association". The call of a class function is not determined at the time of compilation, it is determined at the runtime. When writing the code, it cannot be determined whether the base class function or the derived class function is called, so it becomes a "virtual" function.

A virtual function can achieve polymorphism only by means of pointers or references. If the following code is used, it is a virtual function, but it is not a polymorphism:
Class
{
Public:
   Virtual void Foo ();
};
Class B: public
{
   Virtual void Foo ();
};
Void bar ()
{
   A;
   A. Foo (); // A: Foo () is called.
}

 

1.1 Polymorphism

After understanding the meaning of a virtual function, it is easy to consider what is polymorphism. The above class hierarchy is still targeted, but the method used is more complicated:
Void bar (A *)
{
   A-> Foo (); // is a: Foo () or B: Foo () called ()?
}

Because Foo () is a virtual function, in bar function, you can only use this code to determine whether a: Foo () or B: is called here :: foo (), but it is certain that if a points to an instance of Class A, A: Foo () is called. If a points to an instance of Class B, then B: Foo () is called.

This same code can produce different effects, known as "polymorphism ".

 

1.2 What is the use of polymorphism?
Polymorphism is amazing, but what can it do? This proposition is hard to be summarized in one or two sentences. The General C ++ tutorial (or other object-oriented language tutorial) uses a drawing example to demonstrate the usage of polymorphism, I will not repeat this example again. If you do not know this example, you should introduce it in any book. I try to describe from an abstract point of view, and then look back at the example of the drawing, maybe it is easier for you to understand.

In object-oriented programming, data is first abstracted (determining the base class) and inherited (determining the derived class) to form a class hierarchy. When users of this class hierarchy use them, if they still need to write code for the base class when they need the base class, write the code for the derived class when they need the derived class, it means that the class level is completely exposed to the user. If there is any change in the class hierarchy (new classes are added), the user needs to "know" (write code for the new class ). In this way, the coupling between the class hierarchy and its users is increased. Someone lists this situation as one of the "bad smell" in the program.

Polymorphism can free programmers from this dilemma. Let's look back at the example in 1.1, bar () as the user of the A-B class hierarchy, It doesn't know how many classes there are in this class hierarchy, what each class is called, but the same can work well. When a class C is derived from Class A, bar () does not need to be "known" (modified ). This is entirely due to polymorphism-the compiler generates code for a virtual function that can be called at runtime.

 

1.3 "Dynamic Association"
How does the compiler determine the code of the called function when the virtual function is generated? That is to say, how is a virtual function actually processed by the compiler? In the deep exploration of the C ++ object model [1], Lippman describes several methods. Here we will briefly introduce the "standard" method.

I am talking about the "standard" method, that is, the so-called "vtable" mechanism. When the compiler finds a function declared as virtual in a class, it creates a virtual function table, that is, vtable. Vtable is actually an array of function pointers. Each virtual function occupies a slot of this array. A class has only one vtable, no matter how many instances it has. The derived class has its own vtable, but the vtable of the derived class has the same function arrangement sequence as the vtable of the base class. virtual functions with the same name are placed in the same position of two arrays. When creating a class instance, the compiler adds a vptr field to the memory layout of each instance. This field points to the vtable of the class. Through these methods, the compiler will rewrite this call when it sees a virtual function call. For example in 1.1:
Void bar (A *)
{
   A-> Foo ();
}
Will be rewritten:
Void bar (A *)
{
   (A-> vptr [1]) ();
}

Because the derived class and the Foo () function of the base class have the same vtable index, and their vptr points to different vtables, this method can be used to determine which Foo () to call at runtime () function.

Although the actual situation is far from that simple, the basic principle is roughly the same.

 

1.4 overload and override

Virtual functions are always rewritten in the derived class, which is called "Override ". I often confuse the words "overload" and "Override. But with the increasing number of C ++ books, later programmers may not make any mistakes I have made. But I want to clarify:

Override refers to the virtual function used by the derived class to override the base class, just as the Foo () function in Class A is rewritten in Class B. The rewritten function must have the same parameter table and return value (the C ++ standard allows different return values. I will briefly introduce this in the "Syntax" section, but few compilers support this feature ). It seems that there is no proper Chinese vocabulary for this word. Some people translate it into "Overwrite", which is more appropriate.
Overload conventions are translated as "heavy load ". Compile a function with the same name as an existing function but different from the parameter table. For example, a function can take an integer as a parameter or a floating point as a parameter.

 

Ii. Syntax of virtual functions
The sign of the virtual function is the "virtual" keyword.

 

2.1 use virtual keywords

Consider the following class levels:
Class
{
Public:
   Virtual void Foo ();
};

Class B: public
{
Public:
   Void Foo (); // No virtual keyword!
};

Class C: Public B // inherit from B, not from!
{
Public:
   Void Foo (); // There is no virtual keyword!
};

In this case, B: Foo () is a virtual function, and C: Foo () is also a virtual function. Therefore, it can be said that the virtual function declared by the base class is also a virtual function in the derived class, even if the virtual keyword is no longer used.

 

2.2 pure virtual functions
The following statement indicates that a function is a pure virtual function:
Class
{
Public:
   Virtual void Foo () = 0; // = 0 indicates that a virtual function is a pure virtual function.
};

After a function is declared as pure virtual, the pure virtual function means: I am an abstract class! Don't instantiate me! Pure virtual functions are used to regulate the behavior of derived classes. They are actually called "interfaces ". It tells the user that my Derived classes will all have this function.

 

2.3 virtual destructor
Destructor can also be virtual, or even pure virtual. For example:
Class
{
Public:
Virtual ~ A () = 0; // pure virtual destructor
};

When a class is intended to be used as the basis class of other classes, its destructor must be virtual. Consider the following example:
Class
{
Public:
   A () {ptra _ = new char [10];}
   ~ A () {Delete [] ptra _;} // non-virtual destructor
PRIVATE:
   Char * ptra _;
};
Class B: public
{
Public:
   B () {ptrb _ = new char [20];}
   ~ B () {Delete [] ptrb _;}
PRIVATE:
   Char * ptrb _;
};
Void Foo ()
{
   A * A = new B;
   Delete;
}

In this example, the program may not run as you think. When executing Delete A, there is actually only ::~ A () is called, and Class B's destructor are not called! Is it a little scary?

If the above ::~ Change a () to virtual to ensure that B ::~ B () is also called during Delete. Therefore, the destructor of the base class must be virtual.

Pure virtual destructor have no function, but virtual ones are enough. Generally, when you want to change a class to an abstract class (a class that cannot be instantiated), and this class does not have a suitable function that can be purely virtualized, you can use pure virtual destructor to achieve the goal.

 

2.4 virtual constructor?
Constructors cannot be virtual.

 

Iii. Tips for using virtual functions

3.1 private virtual functions

Consider the following example:
Class
{
Public:
   Void Foo () {bar ();}
PRIVATE:
   Virtual void bar (){...}
};
Class B: public
{
PRIVATE:
   Virtual void bar (){...}
};

In this example, although bar () is private in Class A, it can still appear in the derived class, it can still produce the same polymorphism effect as the public or protected virtual functions. It is not because it is private, so a: Foo () cannot access B: bar (), or B: bar () to :: bar () override does not work.

The syntax is as follows: a tells B that you 'd better override my bar () function, but do not care how it is used or call this function by yourself.

 

3.2 call of virtual functions in constructor and destructor

When a class's virtual functions are called in its own constructor and destructor, they become common functions. That is to say, you cannot make yourself "polymorphism" in constructor and destructor ". For example:
Class
{
Public:
   A () {Foo () ;}// in any case, a: Foo () is called!
   ~ A () {Foo ();} // same as above
   Virtual void Foo ();
};
Class B: public
{
Public:
   Virtual void Foo ();
};
Void bar ()
{
   A * A = new B;
   Delete;
}

If you want to delete a, it will cause B: Foo () to be called, then you will be wrong. Similarly, in the case of new B, the constructor of A is called, but in the constructor of A, A: Foo () instead of B :: foo ().

 

3.4 when to use virtual functions

When you design a base class, if you find that a function needs to behave differently in a derived class, it should be virtual. From the design perspective, the virtual functions that appear in the base class are interfaces, and the virtual functions that appear in the derived class are the specific implementation of interfaces. In this way, the behavior of objects can be abstracted.

Take the factory method mode in design mode [2] as an example. The factorymethod () of the Creator is a virtual function. After the override function of the derived class is used, different product classes are generated, the generated product class is used by the anoperation () function of the base class. The anoperation () function of the base class operates on the product class. Of course, the product class must also have polymorphism (virtual function ).

Another example is a set operation. Suppose you have a class hierarchy based on class A, and you use STD: vector to save instance pointers of different classes in this class hierarchy, therefore, you must not cast each pointer back to its original type (derived class) when operating the classes in this set, but want to perform the same operation on them. Then we should declare this "same operation" as virtual.

In reality, it is far more than the two examples I mentioned, but the big principle is that I mentioned earlier: "If a function needs to have different performances in a derived class, then it should be virtual ". This sentence can also be reversed: "If you find that the base class provides virtual functions, you 'd better override it ".

 

Appendix: usage of virtual and pure virtual functions in C ++

1. virtual functions and pure virtual functions can be defined in the same class. Classes containing pure virtual functions are called abstract classes, and classes containing only virtual functions are called abstract classes) cannot be called abstract class ).

2. virtual functions can be directly used, or they can be called in the form of polymorphism after being overloaded by subclasses. Pure virtual functions must be called in sub classes) can be used only when this function is implemented, because the pure virtual function is in the base class)
Only declarations are not defined.

3. Both virtual and pure virtual functions can be overloaded in sub class and called in the form of polymorphism.

4. virtual functions and pure virtual functions are usually stored in abstract base class-ABC. The inherited subclass is overloaded to provide a unified interface.

5. virtual functions are defined in the form of virtual {method body}; virtual functions are defined in the form of virtual {} = 0; static identifiers are not allowed in the definition of virtual functions and pure virtual functions, the reason is very simple. The static modified function requires bind in the early stage during compilation, but the virtual function is dynamically bound (run-time bind), and the life recycle function modified by both) different.

6. if a class contains pure virtual functions, any statements that attempt to instantiate the class will produce errors, because the abstract base class (ABC) cannot be directly called. The quilt class must inherit from the overload and then call its subclass method as required.

The following is a simple demonstration of virtual functions and pure virtual cold numbers!
// Father class
Class virtualbase
{
Public:
   Virtual void demon () = 0; // Prue virtual function
   Virtual void base () {cout <"this is farther class" <};
};
// Sub class
Class subvirtual: Public virtualbase
{
Public:
   Void demon () {cout <"this is subvirtual! "<Endl ;}

Void base () {cout <"this is subclass base" <Endl ;}
};

Void main ()
{
   Virtualbase * inst = new subvirtual (); // multstate pointer
   Inst-> demon ();
   Inst-> base ();
   // Inst = new virtualbase ();
   // Inst-> base ()
   Return;
}

Bytes ----------------------------------------------------------------------------------------------

 

   A virtual function is a member function declared as virtual in a class. When the compiler sees that such a function is called through a pointer or reference, it is bound to it later, that is, through a pointer (or reference) the type information of the class to which the function belongs. Generally, such pointers or references are declared as base classes. They can refer to objects of base classes or derived classes. Polymorphism means that the same method can have different behaviors based on different objects.

Early binding means that the compiler knows the specific type of the object during compilation and determines the exact address of the member function called by the object; late binding obtains the virtual function table pointer of the Class Based on the type information of the object indicated by the pointer, and then determines the exact address for calling the member function.

The compiler creates a table (vtable) for each class containing virtual functions ). In vtable, the compiler places the virtual function address of a specific class. In each class with a virtual function, the compiler secretly sets a pointer, called vpointer (vptr), to the vtable of this object. When using a base class pointer for a virtual function call (that is, for a multi-state call), the compiler inserts the vptr statically and searches for the code of the function address in the vtable, in this way, the correct function can be called to enable later bundling. Vtable is set for each class, vptr is initialized, and code is inserted for virtual function calls are all automatically generated, so we don't have to worry about this. Using Virtual functions, the appropriate functions of this object can be called, even if the compiler does not know the specific type of the object.

No displayed type information exists in any class, and class information must be stored in the object. Otherwise, the type cannot be created at runtime. What is the class information? Let's look at the following classes:

ClassNo_virtual
{
Public:
    VoidFun1 ()Const {}
    Int Fun2 ()Const{ReturnA;}
PRIVATE:
    IntA;
}

ClassOne_virtual
{
Public:
    VirtualVoidFun1 ()Const {}
    Int Fun2 ()Const{ReturnA;}
PRIVATE:
    IntA;
}

ClassTwo_virtual
{
Public:
    VirtualVoidFun1 ()Const {}
    VirtualInt Fun2 ()Const{ReturnA;}
PRIVATE:
    IntA;
}

Among the above three categories:
No_virtual has no virtual function. sizeof (no_virtual) = 4. The length of class no_virtual is the length of its member variable integer;
One_virtual has a virtual function, sizeof (one_virtual) = 8;
Two_virtual has two virtual functions, sizeof (two_virtual) = 8;There is no difference between the length of a virtual function and the class of two virtual functions. In fact, their length is the length of no_virtual and the length of a void pointer, which reflects, if one or more virtual functions exist, the compiler inserts a pointer in this structure (Vptr ). In one_virtualThere is no difference with two_virtual. This is because vptr points to a table with a storage address and only needs a pointer, because all the virtual function addresses are included in this table. This vptr can be seen as the type information of the class.

Let's see how the compiler creates the virtual function table pointed to by vptr. Let's take a look at the following two classes:
ClassBase
{
Public:
    VoidBfun (){}
    VirtualVoidVfun1 (){}
    VirtualIntVfun2 (){}
PRIVATE:
    IntA;
}
ClassDerived:PublicBase
{
Public:
    VoidDfun (){}
    VirtualVoidVfun1 (){}
    VirtualIntVfun3 (){}
PRIVATE:
    IntB;
}
The virtual function tables (vtables) pointed to by two vptr classes are as follows:
Base Class
          ------
Vptr -->| & Base: vfun1|
           ------
          | & Base: vfun2|
           ------

Derived class
          -------
Vptr -->| & Derived: vfun1|
           -------
          | & Base: vfun2   |
           -------
          | & Derived: vfun3|
            -------

Whenever you create a class that contains a virtual function or derive a class from a class that contains a virtual function, the compiler creates a vtable for this class, as shown in. In this table, the compiler places the addresses of all functions declared as virtual in this class or its base class. If the function declared as virtual in the base class is not redefined in this derived class, the compiler uses the virtual function address of the base class. (In the derived vtable, This is the entry of vfun2 .) Then the compiler places vptr in this class. When simple inheritance is used, there is only one vptr for each object. Vptr must be initialized to point to the corresponding vtable, which occurs in the constructor.

Once the vptr is initialized to point to the corresponding vtable, the object "knows" what type it is. However, this kind of self-cognition is useful only when virtual functions are called.

Vptr is often at the beginning of an object. The compiler can easily obtain the value of vptr to determine the location of the vtable. Vptr always points to the starting address of vtable. The virtual function addresses of all base classes and their subclasses (except virtual functions defined by sub-classes) are always stored in the same vtable, for example, In the vtable of the base and derived classes above, the vfun1 and vfun2 addresses are always stored in the same order. The compiler knows that vfun1 is at vptr and vfun2 is at vptr + 1. Therefore, when using a base class pointer to call a virtual function, the compiler first obtains the type information (vptr) of the pointer to the object ), then call the virtual function. If a base class pointer pbase points to a derived object, then pbase-> vfun2 () is translated by the compilerVptr + 1Because the vfun2 address of the virtual function is located at the Index 1 in the vtable. Similarly, pbase-> vfun3 () is translated by the compilerCall vptr + 2. This is the so-called late binding.

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.