C ++ standard programming: virtual functions and inline functions

Source: Internet
Author: User

When we were discussing C ++, we often asked: "Can virtual functions be declared as inline ?" Now, we can hardly hear this problem. Now I heard: "You should not make print inline. It is wrong to declare a virtual function as inline !"

The two main reasons for this statement are: (1) virtual functions are resolved at runtime, while inline is a compilation action. Therefore, we can declare virtual functions as inline functions without any effect; (2) declaring a virtual function as inline results in multiple-minute copy of the function, and we have spent a storage space for a function that should not be inline at any time. This is hard to understand.

However, this is not the case. Let's take a look at the first one: in many cases, virtual functions are statically determined-for example, when the virtual functions of the base class are called in the virtual functions of the derived class. Why? Encapsulation. An obvious example is the Destructor call chain of the derived class. All virtual destructor, except the virtual destructor that initially triggered the destructor, are static. If we do not inline the virtual destructor of the base class, we cannot profit from it [a]. Is this different from a non-inline virtual destructor? If the hierarchy of the inheritance system is deep and many such class instances need to be destroyed, the answer is yes.

Let's look at another example without destructor. Imagine designing a library class. MaterialLocation is used as a member of the abstract class LibraryMaterial. Declare its print member function as a pure virtual function and provide Function Definition: It outputs MaterialLocation.

Class LibraryMaterial {

Private:

MaterialLocation _ loc; // shared data

//...

Public:

// Declares pure virtual function

Inline virtual void print (ostream & = cout) = 0;

};

// We actually want to encapsulate the handling of

// Location of the material within a base class

// LibraryMaterial print () method-we just don't want it

// Invoked through the virtual interface. That is, it is

// Only to be invoked within a derived class print () method

Inline void

LibraryMaterial ::

Print (ostream & OS) {OS '_ loc ;}

Next, we will introduce a Book class, whose print function outputs Title, Author, and so on. Before that, it calls the print function of the base class (LibraryMaterial: print () to display the book location (MaterialLocation ). As follows:

Inline void

Book ::

Print (ostream & OS)

{

// OK, this is resolved statically,

// And therefore is inline expanded...

LibraryMaterial: print ();

OS title: _ title

"Author" "_ author" endl;

}

The AudioBook class is derived from the Book class and added with additional information, such as descriptions and audio formats. These items are output using its print function. Before that, we need to call Book: print () to display the previous information.

Inline void

AudioBook ::

Print (ostream & OS)

{

// OK, this is resolved statically,

// And therefore is inline expanded...

Book: print ();

OS "narrator:" "_ narrator" endl;

}

This is the same as the example of the virtual destructor call chain, except that the originally called virtual functions are not statically determined, and the others are all expanded in the same place. This unnamed hierarchical design pattern is significantly less valid tive if we never declare a virtual function to be inline.

So what about code expansion in the second reason? Let's analyze it. If we write:

LibraryMaterial * p =

New AudioBook ("Mason & Dixon ",

"Thomas Pynchon", "Johnny Depp ");

//...

P-> print ();

Is this print instance inline? No, of course not. In this way, the virtual mechanism has to be adopted during the runtime. Does this cause the print instance to discard the inline Declaration on it? No. This call is converted to the following form (pseudo code ):

// Pseudo C ++ Code

// Possible transformation of p-> print ()

(* P-> _ vptr [2]) (p );

Where 2 represents the location of print within the associated virtual function table. because print is called through the function pointer _ vptr [2], the compiler cannot statically determine the call address, and the function cannot be inline.

Of course, the inline entity (definition) of the virtual function print must also be displayed somewhere. That is to say, at least one function entity is expanded in the same place as the address called by virtual table. How does the compiler determine when to expand this function entity? One of the implementaion policies is to generate this function entity when the virtual table is generated. This means that a function entity is generated for the virtual table of each derived class.

How many vitrual tables are generated in a class [B] that can be applied? Haha, This is a good question. The C ++ Standard specifies the virtual function behavior, but does not specify the function implementation. Because virtual table is not defined in the C ++ standard, it is obvious that there are no rules on how to generate this virtual table or how many vitrual tables to generate. How many? Of course, we only need one. The cfront compiler of Stroustrup cleverly handles these situations. (Stan and Andy Koenig described the algorithm in the March 1990 C ++ Report article, "Optimizing Virtual Tables in C ++ Release 2.0 .")

Moreover, the C ++ Standard now requires that inline functions behave as though only one definition for an inline function exists in the program even though the function may be defined in different files. The new rule requires the compiler to expand only one inline virtual function. If it is widely used, the Code expansion problem caused by inline function will disappear.

[: C ++ Standard: 9.3.8, Member function of local class shall be defined inline in their class defination, if they are defined at all]

======================================

:

[A] function call overhead. At least two indirect processes are required to call a base-class virtual function (S. B. Lippman: Inside the C ++ Object Model).

[B] A product class (?)

Summary:

That is, the virtual function inline is useful in call chains and other places ~

Even if the inline declaration is not added, it will be optimized (Virtual destructor) as a good compiler)

In a long function call chain, it is best to set the basic function inline in the chain to save overhead.

As to where inline is located, it is determined by the compiler because the C ++ standard does not stipulate

The new C ++ standard (may not pass) stipulates that inline is only valid for the product class and only takes one action

Ensures that the Code is not full

The inline action is generated together with the vtable when the product class is instantiated.

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.