C++: Why does a struct\class need a virtual method in order to be polymorphic?
Asked Answered
F

8

17

Following this question, I'm wondering why a struct\class in C++ has to have a virtual method in order to be polymorphic.

Forcing a virtual destructor makes sense, but if there's no destructor at all, why is it mandatory to have a virtual method?

Foggia answered 29/4, 2011 at 12:13 Comment(2)
Because that's the way the language was designed. If you are interested in this sort of thing, read Stroustrup's D&E book.Antifriction
@Antifriction Answers like "because that's the way it was designed" do nothing but change the question to "well why was it designed that way?"Nisbet
G
24

Because the type of a polymorphic object in C++ is, basically, determined from the pointer to its vtable, which is the table of virtual functions. The vtable is, however, only created if there's at least one virtual method. Why? Because in C++, you never get what you didn't explicitly ask for. They call it "you don't have to pay for something you don't need". Don't need polymorphism? You just saved a vtable.

Gastrin answered 29/4, 2011 at 12:18 Comment(8)
vtable is not necessary for polymorphism!Tamera
@Nawaz: You said In C++, runtime polymorphism is achieved through virtual functions. So?Centrepiece
@sad_man: virtual functions and vtable are two different things.Tamera
@Nawaz: Yes but aren't they related?Centrepiece
@sad_man: What do you mean by "related"? All I'm saying is that polymorphism can be implemented in different ways as well. Most compilers happen to use vtable and vptr concept to implement this, but that isn't mandated by the C++ Standard.Tamera
@sad_man: see this discussion : How can C++ virtual functions be implemented except vtable?Tamera
@Nawaz: I actually didn't know that vtables aren't mandatory in C++. On the other hand, I think it doesn't matter for me. Standards are a good thing, but if it doesn't compile on my target compilers, I couldn't care less about the standard :) Dealing with these things on mainstream compilers (i.e. GCC and VC++) is sometimes hard enough, but if the very same code must run on some strange commercial embedded compilers, it gets really nasty...Gastrin
@Nawaz, you may not need a vtable but you'll still need some way to identify the class. This means adding some extra field to it. Thus you'd still need to distinguish the classes with virtual and those without. Thus to be pedantic replace vtable in the answer with compiler-defined-virtual-identity-system. ;)Ethyne
B
8

Forcing a virtual destructor makes sense

Exactly. To destruct a virtual class manually (via delete) through its base class you need a virtual destructor. (Now, as I’ve been reminded in the comments, this isn’t usually needed: rather than use manual memory management, one would rely on modern smart pointers which also work correctly with non-virtual destructors.)

So any class which acts as a polymorphic base class usually needs either a virtual destructor or virtual functions anyway.

And since having runtime polymorphism adds an overhead (the class needs to store an additional pointer to its virtual method table), the default is not to add it, unless necessary anyway: C++’ design philosophy is “you only pay for what you need”. Making every class have a virtual method table would run afoul of this principle.

Boutte answered 29/4, 2011 at 12:17 Comment(5)
Couldn't agree more. What makes me sad is that, technically, C++ does not force a virtual destructor, which can make for strange (albeit exactly defined) behaviour when forgetting to make it virtual...Gastrin
I cannot agree. You only need a virtual destructor if you want to destruct polymorphically, the rule being that you should offer either a public virtual destructor or a protected non-virtual one, where the second option allows you to create and delete objects non-polymorphically but use them polymorphically while they are alive. As a matter of fact, a class whose only virtual method is the destructor can only be used polymorphically for destruction.Modie
@David Agreed but I wanted to restrict my answers to public destructors (the usual use-case) to not distract from the central point.Boutte
you may see polymorphic deletion as the usual use-case, but I think it is quite rarely justified. If an object is managed by the class that creates it, or if ownership is transferred using shared_ptr or unique_ptr (if available), then the object can be correctly deleted non-polymorphically. It is only needed if its owner stores it in a container of raw base-class pointers, or if ownership is transferred using auto_ptr or a raw pointer, neither of which I would regard as typical use cases. On the other hand, I tend to avoid inheritance, so my use cases may differ from others'.Ambroid
@Mike Hmm. You’re right. The “new-style” smart pointers are a game changer in this regard. My “default” use-case stems from olden times.Boutte
B
3

Because it is defined as such in the standard.

From 10.3/1 [class.virtual]

Virtual functions support dynamic binding and object-oriented programming. A class that declares or inherits a virtual function is called a polymorphic class.

It makes sense that if you use inheritance, then you have at least one virtual method. If you don't have any virtual method, then you could use composition instead.

Bridle answered 29/4, 2011 at 13:28 Comment(0)
P
2

polymorphism is to allow your subclasses to override the default behaviour of base class functions, so unless you have virtual methods in your base class, you can't override methods in base.

Premedical answered 29/4, 2011 at 12:17 Comment(10)
+1, this is actually the reason. There is much confusion on what a polymorphic class is, but really, the class is not polymorphic, it exhibits polymorphic behavior through virtual functions. As a matter of fact, a class with only the destructor being polymorphic can hardly be used polymorphically (for anything other than destruction).Modie
What I don't understand with this explanation is this: Suppose you have a base class A, which you just want to sort things out (i.e. create a list of certain objects). Now you derive B from A, and add some virtual functions, which are overridden in C (which is derived from B). Now A is not polymorphic, yet you can store objects of type C in a collection of type A. Wouldn't it make sense, to force A to be polymorphic (dynamic_cast-able)? C# and Java do this, by having a general common base class Object, which makes things much easier. But then, C++ is not supposed to be easy, I guess.Gastrin
@OregonGhost: It's not that C++ is supposed to be difficult, it's that in C++ you don't pay for what you don't use (and this makes it difficult in that you have to decide what you're using and what you aren't). In fact you cannot store objects of type C in a collection of type A. You can store pointers to objects of type C in a collection of pointers to objects of type A, though. If you want to be able to make virtual calls using those pointers-to-A, then you give A some virtual functions and make the calls. If you don't, then you don't.Riobard
@Steve Jessop: I meant storing pointers. My point is, in my example I don't want to make virtual calls using pointers-to-A, A is just there as a restriction on what you can put into the list, just a hypothetical example. Regarding don't pay for what you don't use: I can't hear that anymore. Everytime I have to do a lot of stupid work just to make C++ work correctly, someone says, yeah, it's so you don't have to pay for what you don't use. I don't care, honestly, I want to get things done ;) It's not difficult to decide what you don't need, it's a lot of work to use the things you do need.Gastrin
Actually, we're basically using C++ only on Embedded platforms (with embedded also being small Linux or Windows CE based systems). On Desktop Windows or Linux, if it has to be C++, it's usually Qt, which hides a lot of the ugliness of C++. But even with Qt, a lot of things just have to be done, because there may be someone who doesn't need them. Sigh. I digress. I'd rather have A be always polymorphic in my example, so I don't have to care.Gastrin
@OregonGhost: no problem, just that if you want a language in which all member functions are virtual, use Java or C#. If you don't like C++'s "lean" philosophy, don't use C++ :-) It is a slight niggle, though, that if you don't want to make virtual calls though A*, but you do want to dynamic_cast from A*, then A needs a useless virtual function just to make it polymorphic (usually the destructor). That's pretty rare, though, that you'd want to store in a container using a base class pointer, but don't want to make virtual calls.Riobard
@Steve Jessop: Yes, I avoid C++ wherever I can - so I don't have to pay for something (in manhours) I don't need :D But since I'm still doing C++ work (not only maintenance, also new code), I have to live with it :)Gastrin
@OregonGhost: OK. It doesn't take me hours to write virtual ~A() {}, but YMMV ;-p. Then again, I very rarely use dynamic polymorphism in C++, so I'm exactly the person that you're paying for.Riobard
@Steve Jessop: Writing an empty virtual destructor may just take a second (for every class that needs it, though). If this was the only thing that C++ makes different or more complicated, I would be fine. By the way, we're looking for one or two C++ developers ;)Gastrin
Personally I can get tired on the "all methods are virtual" in Java. I get tired to sometimes dive through layer after layer of hierarchy for methods which may or may not have been overridden and where @override is often "optimized away" in terms use everything and pay for nothing :(Vivisection
T
2

I'm wondering why does a struct\class in C++ has to have a virtual method in order to be polymorphic?

Because that is what polymorphic class means.

In C++, runtime polymorphism is achieved through virtual functions. A base class declares some virtual functions which the many derived classes implement, and the clients use pointers (or references) of static type of base class, and can make them point to objects of derived classes (often different derived classes), and then later on, call the implementation of derived classes through the base pointers. That is how runtime polymorphism is achieved. And since the central role is played by the functions being virtual, which enables runtime polymorphism, that is why classes having virtual functions is called polymorphic class.

Tamera answered 29/4, 2011 at 12:18 Comment(0)
M
1

Without any virtual method, there is no need to maintain a virtual pointer (abbreviated as vptr) for every object of the class. The virtual pointer is a mechanism for resolving virtual method calls at runtime; depending on the object's class, it might point to different virtual method tables (abbreviated as vtable) that contain the actual adresses of virtual methods.

So by checking to what vtable does the vptr point to, compiler can determine object's class, for example in dynamic_cast. Object without the vptr cannot have its type determined this way and is not polymorphic.

Mitchel answered 29/4, 2011 at 12:21 Comment(0)
C
1

A C++ design philosophy is that "you don't pay for what you don't use". You might already know that a virtual function incur some overhead as a class has to maintain a pointer to its implementation. In fact, an object contains a reference to a table of function pointers called the vtable.

Consider the following example:

class Base
{
public:
    virtual f() { /* do something */ }
}; 

class Derived : public Base
{
public:
    virtual f() { /* do something */ }
}; 

Base* a = new Derived;
a->f(); // calls Derived::f()

Note that the variable a points to a Derived object. As f() is declared virtual the vtable of a will contain a pointer to Derived::f() and that implementation is executed. If f() is not virtual, the vtable will be empty. So Base::f() is executed as the type of a is Base.

A destructor behaves just like other member functions. If the destructor is not virtual, only the destructor in the Base class will be called. This might lead to memory/resource leaks if the Derived class implements RAII. If a class is intended to be sub-classed, its destructor should be virtual.

In some languages like Java all methods are virtual. So even objects that are not intended to be polymorphic will consume memory for maintaining the function pointers. In other words, you are forced to pay for what you don't use.

Commonwealth answered 29/4, 2011 at 12:47 Comment(0)
R
0

Classes only need virtual methods in order to be dynamically polymorphic - for the reasons described by others. You can still have static polymorphism through templates, though.

Roadhouse answered 29/4, 2011 at 12:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.