



Yes. It's sometimes (not always!) a great idea. For example, suppose all Shape objects have a common algorithm for printing, but this algorithm depends on their area and they all have a potentially different way to compute their area. In this case Shape's area() method would necessarily have to be virtual (probably pure virtual) but Shape::print() could, if we were guaranteed no derived class wanted a different algorithm for printing, be a non-virtual defined in the base class Shape.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
Yes, it is a different strategy. Yes, there really are two different basic ways to use virtual functions:
For emphasis, the above list is a both/and situation, not an either/or situation. In other words, you don't have to choose between these two strategies on any given class. It's perfectly normal to have method f() correspond to strategy #1 while method g() corresponds to strategy #2. In other words, it's perfectly normal to have both strategies working in the same class.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

Because that would be very dangerous, and C++ is protecting you from that danger.
The rest of this FAQ gives a rationale for why C++ needs to protect you from that danger, but before we start that, be advised that you can get the effect as if dynamic binding worked on the this object even during a constructor via The Dynamic Binding During Initialization Idiom.
First, here is an example to explain exactly what C++ actually does:
The output from the above program will be:
The rest of this FAQ describes why C++ does the above. If you're happy merely knowing what C++ does without knowing why, feel free to skip this stuff.
The explanation for this behavior comes from combining two facts:
Now some of you are still curious, saying to yourself, "Hmmmm, but I still wonder why the this object is merely of type Base during Base::Base()." If that's you, the answer is that C++ is protecting you from serious and subtle bugs. In particular, if the above rule were different, you could easily use objects before they were initialized, and that would cause no end of grief and havoc.
Here's how: imagine for the moment that calling this->virt() within Base::Base() ended up invoking the override Derived::virt(). Overrides can (and often do!) access non-static data members declared in the Derived class. But since the non-static data members declared in Derived are not initialized during the call to virt(), any use of them within Derived::virt() would be a "use before initialized" error. Bang, you're dead.
So fortunately the C++ language doesn't let this happen: it makes sure any call to this->virt() that occurs while control is flowing through Base's constructor will end up invoking Base::virt(), not the override Derived::virt().
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

Yes.
To clarify, we're talking about this situation:
This FAQ shows some ways to simulate dynamic binding as if the calls made in Base's constructor dynamically bound to the this object's derived class. The ways we'll show have tradeoffs, so choose the one that best fits your needs, or make up another.
The first approach is a two-phase initialization. In Phase I, someone calls the actual constructor; in Phase II, someone calls an "init" method on the object. Dynamic binding on the this object works fine during Phase II, and Phase II is conceptually part of construction, so we simply move some code from the original Base::Base() into Base::init().
The only remaining issues are determining where to call Phase I and where to call Phase II. There are many variations on where these calls can live; we will consider two.
The first variation is simplest initially, though the code that actually wants to create objects requires a tiny bit of programmer self-discipline, which in practice means you're doomed. Seriously, if there are only one or two places that actually create objects of this hierarchy, the programmer self-discipline is quite localized and shouldn't cause problems.
In this variation, the code that is creating the object explicitly executes both phases. When executing Phase I, the code creating the object either knows the object's exact class (e.g., new Derived() or perhaps a local Derived object), or doesn't know the object's exact class (e.g., the virtual constructor idiom or some other factory). The "doesn't know" case is strongly preferred when you want to make it easy to plug-in new derived classes.
Note: Phase I often, but not always, allocates the object from the heap. When it does, you should store the pointer in some sort of managed pointer, such as a std::auto_ptr, a reference counted pointer, or some other object whose destructor deletes the allocation. This is the best way to prevent memory leaks when Phase II might throw exceptions. The following example assumes Phase I allocates the object from the heap.
The second variation is to combine the first two lines of the joe_user function into some create function. That's almost always the right thing to do when there are lots of joe_user-like functions. For example, if you're using some kind of factory, such as a registry and the virtual constructor idiom, you could move those two lines into a static method called Base::create():
This simplifies all the joe_user-like functions (a little), but more importantly, it reduces the chance that any of them will create a Derived object without also calling init() on it.
If you're sufficiently clever and motivated, you can even eliminate the chance that someone could create a Derived object without also calling init() on it. An important step in achieving that goal is to make Derived's constructors, including its copy constructor, protected or private..
The next approach does not rely on a two-phase initialization, instead using a second hierarchy whose only job is to house methods foo() and bar(). This approach doesn't always work, and in particular it doesn't work in cases when foo() and bar() need to access the instance data declared in Derived, but it is conceptually quite simple and clean and is commonly used.
Let's call the base class of this second hierarchy Helper, and its derived classes Helper1, Helper2, etc. The first step is to move foo() and bar() into this second hierarchy:
Next, remove init() from Base (since we're no longer using the two-phase approach), remove foo() and bar() from Base and Derived (foo() and bar() are now in the Helper hierarchy), and change the signature of Base's constructor so it takes a Helper by reference:
We then define Base::Base(const Helper&) so it calls h.foo(42) and h.bar() in exactly those places that init() used to call this->foo(42) and this->bar():
Finally we change Derived's constructor to pass a (perhaps temporary) object of an appropriate Helper derived class to Base's constructor (using the init list syntax). For example, Derived would pass an instance of Helper2 if it happened to contain the behaviors that Derived wanted for methods foo() and bar():
Note that Derived can pass values into the Helper derived class's constructor, but it must not pass any data members that actually live inside the this object. While we're at it, let's explicitly say that Helper::foo() and Helper::bar() must not access data members of the this object, particularly data members declared in Derived. (Think about when those data members are initialized and you'll see why.)
Of course the choice of which Helper derived class could be made out in the joe_user-like function, in which case it would be passed into the Derived ctor and then up to the Base ctor:
If the Helper objects don't need to hold any data, that is, if each is merely a collection of its methods, then you can simply pass static member functions instead. This might be simpler since it entirely eliminates the Helper hierarchy.
The Derived class is also easy to implement:
As before, the functionality for foo() and/or bar() can be passed in from the joe_user-like functions. In that case, Derived's ctor just accepts them and passes them up into Base's ctor:
A final approach is to use templates to "pass" the functionality into the derived classes. This is similar to the case where the joe_user-like functions choose the initializer-function or the Helper derived class, but instead of using function pointers or dynamic binding, it wires the code into the classes via templates.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

It's legal, but it ain't moral.
Experienced C++ programmers will sometimes redefine a non-virtual function for efficiency (e.g., if the derived class implementation can make better use of the derived class's resources) or to get around the hiding rule. However the client-visible effects must be identical, since non-virtual functions are dispatched based on the static type of the pointer/reference rather than the dynamic type of the pointed-to/referenced object.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]

It means you're going to die.
Here's the mess you're in: if Base declares a member function f(int), and Derived declares a member function f(float) (same name but different parameter types and/or constness), then the Base f(int) is "hidden" rather than "overloaded" or "overridden" (even if the Base f(int) is virtual).
Here's how you get out of the mess: Derived must have a using declaration of the hidden member function. For example,
If the using syntax isn't supported by your compiler, redefine the hidden Base member function(s), even if they are non-virtual. Normally this re-definition merely calls the hidden Base member function using the :: syntax. E.g.,
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
If you get a link error of the form "Error: Unresolved or undefined symbols detected: virtual table for class Fred," you probably have an undefined virtual member function in class Fred.
The compiler typically creates a magical data structure called the "virtual table" for classes that have virtual functions (this is how it handles dynamic binding). Normally you don't have to know about it at all. But if you forget to define a virtual function for class Fred, you will sometimes get this linker error.
Here's the nitty gritty: Many compilers put this magical "virtual table" in the compilation unit that defines the first non-inline virtual function in the class. Thus if the first non-inline virtual function in Fred is wilma(), the compiler will put Fred's virtual table in the same compilation unit where it sees Fred::wilma(). Unfortunately if you accidentally forget to define Fred::wilma(), rather than getting a Fred::wilma() is undefined, you may get a "Fred's virtual table is undefined". Sad but true.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
This is known as making the class "final" or "a leaf." There are two ways to do it: an easy technical approach and an even easier non-technical approach.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
This is known as making the method "final" or "a leaf." Here's an easy-to-use solution to this that gives you 90+% of what you want: simply add a comment next to the method and rely on code reviews or random maintenance activities to find violators. The comment could say, for example, // We'll fire you if you override this method or perhaps more likely, /*final*/ void theMethod();.
The advantages to this technique are (a) it is extremely easy/fast/inexpensive to use, and (b) it is quite effective in practice. In other words, you get 90+% of the benefit with almost no cost lots of bang per buck.
(I'm not aware of a "100% solution" to this problem so this may be the best you can get. If you know of something better, please feel free to email me. But please do not email me objecting to this solution because it's low-tech or because it doesn't "prevent" people from doing the wrong thing. Who cares whether it's low-tech or high-tech as long as it's effective?!? And nothing in C++ "prevents" people from doing the wrong thing. Using pointer casts and pointer arithmetic, people can do just about anything they want. C++ makes it easy to do the right thing, but it doesn't prevent espionage. Besides, the original question (see above) asked for something so people won't do the wrong thing, not so they can't do the wrong thing.)
In any case, this solution should give you most of the potential benefit at almost no cost.
[ Góra | Dół | Poprzednia sekcja | Następna sekcja | Szukaj w FAQ ]
E-mail the author
[ C++ FAQ Lite
| Spis treści
| Skorowidz
| O autorze
| ©
| Pobierz swoją własną kopię ]
Ostatnia aktualizacja Jun 17, 2002
Wersja
polska: 0.1h Oct 3, 2003