Q |
A |
The answer is, it depends. To see why, let's consider each case—inline and virtual—separately. Normally, an inline function is expanded, well, inline.
class CFoo { private: int val; public: int GetVal() { return val; } int SetVal(int v) { return val=v; } }; |
In this situation, if you write
CFoo x; x.SetVal(17); int y = x.GetVal(); |
then the compiler generates the co
CFoo x; x.val = 17; int y = x.val; |
Of course you can't do this because val is private. Inline functions give you the advantage of da
Virtual functions give you polymorphism, which means derived classes can implement the same function differently. Suppose GetVal is now declared virtual and you have a second class, CFoo2, with a different implementation:
class CFoo2 : public CFoo { public: // virtual in base class too! virtual int CFoo2::GetVal() { return someOtherVal; } }; |
If pFoo is a pointer to a CFoo or CFoo2, then pFoo->GetVal will call the member function for whichever class pFoo really points to—CFoo or CFoo2. This is C++ 101, and you should know it like the back of your hand.
But what happens if a function is both virtual and inline? Remember, there are two ways to make a function inline: by using the inline keyword in the function definition, as in
inline CFoo::GetVal() { return val; } |
or by coding the function body inline within the class declaration, as in the previous CFoo2::GetVal example. So if you include the body of your virtual function within the class declaration
class CFoo { public: virtual int GetVal() { return val; } }; |
then you are telling the compiler you want GetVal to be both inline and virtual. This doesn't seem to make sense, for how can polymorphism work if the function is expanded inline? The answer, of course, is that it can't. Or can it?
The first rule the compiler follows when it encounters such a beast is this: whatever happens, polymorphism must work. If you have a pointer to a CFoo object, then pFoo->GetVal is guaranteed to call the right function. In general, this means the GetVal functions will be instantiated as real (noninline) functions, with vtable entries pointing to them. But that doesn't mean the function can't ever be expanded! Consider this co
CFoo x; x.SetVal(17); int y = x.GetVal(); |
The compiler knows that x is really a CFoo, not a CFoo2, since the stack object is explicitly declared. There's no way x could really be a CFoo2. So it's safe to expand SetVal/GetVal inline. If you write this more complex co
CFoo x; CFoo* pfoo=&x; pfoo->SetVal(17); int y = pfoo->GetVal(); ??? CFoo2 x2; pfoo = &x2; pfoo->SetVal(17); //etc. |
the compiler knows that pfoo points to x the first time and x2 the second time, so again it's safe to expand the virtual functions. You can dream up ever more complex examples, where the type of object pfoo points to is always known, but most compilers won't do any heavy analysis. Even in the preceding example, some compilers will do the safe thing, which is to instantiate the function and call through a vtable. Indeed, the compiler is free to ignore the inline requirement and always use the vtable. The on
In general, inline—whether explicit or implicit—is a hint, not a mandate, just like register. (Does anyone remember register? It asks the compiler to use a machine register for a variable if it can.) The compiler can refuse to expand even a nonvirtual inline function if it wants to, and the first C++ compilers often complained, "inline aborted—function too big." Certainly if an inline function calls itself, or if you pass its address somewhere, the compiler must generate a normal (outline?) function. And, of course, inline functions are not expanded in debug builds, or if you set a compiler option preventing it.
The on
The topic of inline functions is more complex than you might think at first. There are many circumstances that force the compiler to generate a normal function: recursion, taking the address of your function, functions that are too big, and virtual functions. Here's another consideration: if the compiler decides to instantiate your inline function, where does it put the function? Which module does it go into?
Usually, classes are declared in header (.h) files. So if mumble.cpp includes foo.h and the compiler decides it has to instantiate CFoo:: GetVal, it will instantiate it as a static function in mumble.cpp. If 10 modules include foo.h, the compiler could generate up to 10 copies of your virtual function. In fact, you could end up with objects of the same type with vtables pointing to different copies of GetVal. Yuk! Some linkers are smart enough to eliminate the redundancies at link time, but in general you can't be sure.
So the bottom line is: it's best not to use inline virtual functions, since they're almost never expanded anyway. Even if your function is just on
http://msdn.microsoft.com/msdnmag/issues/0600/c/
Today, I made a bug by using inline virtual functions. But I really didn't know it is a inline virtual function, as there is no virtual key word before the function declare of a member in .h file. The function is a virtual function in the base class, but there isn't any virtual key word in the sub class. Gosh, whaterver it is! why not new and override as C sharp the super language?
Today, I made a bug by using inline virtual functions. And I sware I won't forget vitual anymore but must inline function.
评论