TITLE: Virtual functions and inline PROBLEM: Is it bad design to declare virtual functions as inline? C++ seems to allow this. RESPONSE: Steve Simmons (???) In the first place, you are never guaranteed that a routine is inlined. It is only a suggestion to the compiler. If the routine is either too complicated or a virtual function, then a static copy of the routine will be placed in the compiled module. Thus, a routine that was coded as inline may cause a performance degradation because it may consume much more space when it is not physically inlined. With a virtual function, a copy of that routine will be created for every module that has at least one instantiation of that class. RESPONSE: Neil (Neil@teleos.com) I believe that if it is the "virtualness" which makes the function into a bottleneck, it will not be revealed in the profile, as the virtual function overhead takes place in the calling function, where the extra pointer dereferencing takes place. The profiler only shows how long was spent in the function itself; it *cannot* attribute any overheads spent in the function calling sequence to the called function. However, in every compiler I have used, virtual functions are implemented using a table of function pointers. [ ...stuff deleted... ] This has an overhead of *two* machine instructions, or less than a microsecond! Really, I don't buy factors of 100. Worst case, if (a) the function in question is a do-nothing access function, (b) it could have been inlined and removed altogether, (c) your whole program is spent calling this function, then I might expect a factor of two or at most three speed-up by avoiding the virtual function. Of course, if the virtual function call also puts the code in some loop outside code-cache limits, you might see a much larger factor, but *that* is not a feature of virtual functions -- any code could do the same. [ ...stuff deleted... ] I would make three points: 1. Joe is right: when in doubt, make it virtual -- especially destructors. 2. Use the profiler -- it wont show you virtual function overhead, but then there isn't much to be concerned about there. It is always better to leave the the majority of the code simple and clear, and spend effort where it is needed, as indicated by profiling. 3. Avoid (explicit or implicit) inline declarations of virtual functions in header files, as these cause a whole static copy of the vtable for the class, as well as code for each such function, to be created for at least every file in which the constructor is called: class Bad : public Base { virtual int Status() { return 2; } // BAD }; Instead, put the implementation of the function in one .C file. RESSPONSE: Robert C. Martin (rmartin@rational.com) [ ... stuff deleted... ] This is a space/time trade-off. The example shown above is not always "BAD" as the comment would lead us to believe. If you can afford the space generated by a vtbl in every translation unit (small in comparison to the code itself) and if you can afford the space required by the inline expansion of a function, and if you need the speed, then by all means use inline functions. BTW the example above could require significantly less space than its non-inline counterpart since the whole thing boils down to a literal '2' which can be incorporated as part of a single machine instruction. If this function were called alot, the space savings (not to mention the speed savings) would far outweigh the cost of the extra vtbl. RESPONSE: Joe Buck (jbuck@ohm.berkeley.edu) Most compiler implementations provide a way to get around this (for example, both cfront and g++ do). RESPONSE: David Bonn (bonn@underdog) There are some cases where inline virtual functions make sense. Consider: class sometype; class BASE { public: BASE(void); ~BASE(void); virtual sometype *grab_stuff(void); // ... other declarations too numerous to mention }; class DERIVED : public BASE { public: DERIVED(void); ~DERIVED(void); sometype *grab_stuff(void); // ... other declarations too numerous to mention }; // ... somewhere else in the great source code pit sometype *DERIVED::grab_stuff(void) { sometype *stuff = BASE::grab_stuff(); dork_with(stuff); return stuff; } In summary, it is pretty common for a derived class function to call the base class version of that function and do some extra stuff. Since the function name is fully qualified, the compiler can (and should!) figure out that it can be inlined. Virtual destructors can also work this way. RESPONSE: Bjarne Stroustrup (bs@alice.att.com), 24 Jul 92 Actually, you can take the address of an inline function. The semantics of taking the address of an inline is that a non-inline copy of the function is layed down and its address is then used. In some implementations this can lead to memory overhead caused by "outline inlines" and in many calls inlining cannot be done for an inline virtual (because it cannot be known at compile time exactly which f() is to be invoked). For example: void (A* p, A obj) { // cannot inline (*p might be of a class derived from A // with a different f() ) p->f(); // can inline obj.f() }