Standard C programming: virtual function and inline
Josée Lajoie and Stanley Lippman
-------------------------------------------------- --------------------------------
[This is the last installment of a column that was being published in C Report magazine Since the magazine ceased publication before this installment could be published, Josée Lajoie and Stan Lippman were gracious enough to let us publish it on the CUJ website -.. Mb ]
Once, we often talk about C to hear a problem: "The virtual function should really be declared as inline?" Now, we have fewer hearing this problem. In turn, we have heard now "You should not put the print () function. State the virtual function to the inline is wrong."
This is said that there are two main reasons: (1) The virtual function is at the runtime judgment, and the inline is compiled, so it cannot be adopted from this (inline); (2) Staff the virtual function Inline will cause this function to have multiple copies in the executable, so we pay a punishment in space for a function that cannot be inlined, in contrast (WQ note, so-called inline function non-inline problem). Obviously there is no brain.
Just it is not true. Reflecting Reasons (1): In many cases, the virtual function is a static judgment - especially the derived virtual function calls its base class version. Why do you do that? Package. A good example is a static call chain of a destructuring function: the destructor of the base class is triggered by the destructor of the derived class. In addition to the initial one, all destructor calls are static. Do not let the base class's false argument function inline, it is not possible to benefit from it. Is this a big difference? If the inheritance level is very deep, there is a lot of objects that require destructive, (the answer is) "Yes".
Another example does not involve a destructive function. Imagine that we are designing a library borrowing management program. We have placed "position" into abstract class librarymaterial. When the print () function is a pure virtual function, we also provide its definition: print out the location of the object.
Class LibraryMaterial {
Private:
MaterialLocation _loc; // Shared Data
// ...
PUBLIC:
// Declares Pure Virtual Function
Inline Virtual Void Print (Ostream & = Cout) = 0;
}
// Weistually Want to Encapsulate the Handling of The
// Location of the Material forin 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;
Then introduce the Book class; its print () function will output the title, author, and the like. Before this, it first invigoes the librarymaterial :: print () function of the base class to display location information. E.g:
Inline void
Book ::
PRINT (Ostream & OS)
{
// ok, this is resolved staticly,
// and there is inline expanded ...
Librarymaterial :: print ();
OS << "Title: << _title
<< "Author" << _author << Endl;
}
The Audiobook class is derived from BOOK, introducing a two choice of borrowing strategy, and add some additional information, such as tandem, format, and more. These will be displayed in its print () function. Before displaying these, it calls /k :: print ():
Inline void
Audiobook ::
PRINT (Ostream & OS)
{
// ok, this is resolved staticly,
// and there is inline expanded ...
Book :: print ();
OS << "Narrator:" << _narrator << Endl;
}
In this example and the description function, the false method of the derived class is incremented to extend its base class version of the function, and is called in a call chain, only the initial call is determined by the virtual system. This has no inheritance tree design mode that is named. If you never declare the virtual function as inline, it will obviously be a little inefficient.
How do you say about the code expansion of reasons (2)? Ok, think about it. If you write,
LibraryMaterial * P =
New Audiobook ("Mason & Dixon",
"Thomas Pynchon", "Johnny DEPP");
// ...
P-> Print ();
Is the Print () herein inline? No, of course it will not. This must pass the virtual system of the runtime. Okay. It will cause the print () function here to have its own definition body? Not. Call is compiled into this form:
// pseudo C code
// Possible Transformation of P-> Print ()
(* p -> _ vptr [2]) (p);
That 2 is the position of the Print () function in the corresponding virtual function table. Because this call to print () is done by the function pointer _vptr [2], the compiler cannot staticly decide the position of the called function, and the function cannot be inline.
Of course, the definition of the inline virtual function print () must appear somewhere in the executable, the code can be executed correctly. That is, at least one defined body is required to place its address into the virtual function table. How do the compiler decide when to generate that defined body? A implementation strategy is to generate that definition when generating the virtual function table of that class. This means that one instance of each inline virtual function is also generated for each virtual function table instance generated for a class.
In the executable, how many virtual functions generated for a class? Ah, very good, good question. The C standard specifies the requirements of virtual functions in behavior; but it does not specify the requirements on virtual functions. Since the existence of the virtual function table is not required by the C standard, the obvious criteria have not further requested how to handle virtual function tables and how many times. The best number is of course "once". For example, Stroustrup's original CFRONT implementation version is intelligent in most cases. (Stan and Andy Koenig) describing its algorithm, published in March 1990, C Report, "Optimizing Virtual Tables IN C Release 2.0.") In addition, the C standard now requires the behavior of the inline function to meet only the seemingly program A definition body, even if this function may be defined in a different file. The new rule is to meet the requirements of the specified implementation, and the behavior should seem to have only one instance. Once the standard is widely adopted, the concerns of the potential code expansion of the inner function should disappear.
There is a conflict in the C community: teaching requires rules as simple inspection tables need to use rules in accordance with the environment in VS practices. The former is a response to the complexity of the language; the latter is a response to the complexity of our constructed solution. When to declare the virtual function as an inline problem, it is a good example of this conflict.
About the authors
Stanley Lippman was the software Technical Director for the Firebird segment of Disney's Fantasia 2000. He was recently technical lead on the ToonShooter image capture and playback system under Linux for DreamWorks Feature Animation and consulted with the Jet Propulsion Laboratory. He is currently IT Training Program Chair For You-Niversity.com, An E-Learning Training Company. He Can Be Reached At Stanleyl @ you-nance, www.you-neventiTy.com, and www.objectwrite.com.
Josée Lajoie is currently doing her Master's degree in Computer Graphics at the University Waterloo. Previously, she was a member of the C / C compiler development team at the IBM Canada Laboratory and was the chair of the core language working group for the ANSI / ISO C Standard Committee. She Can Be Reached At Jlajoie@cgl.uwaterloo.ca.