2

Example: We have a base class Base and three subclasses which all implement their own version of doSomething().

In an intermediate function f(Base b), we want to call the relevant version of doSomething() depending on which object has been passed.

Why do we need to declare doSomething as a virtual function in Base? Surely b now points to the correct object so that when we call b.doSomething, we know which method needs to be called.

Side Question: If b is simply pointing to an object, why does it even need a type?

Oddly enough, this once made perfect sense but I'm starting to have doubts, and questions are arising…

12

Because virtual functions have a runtime performance cost, and part of the philosophy of C++ is "you don't pay for what you don't use."

Specifically, virtual methods have to dereference a pointer to find out where the correct method is for the specific object they were called on. One pointer dereference per call may not sound like a lot, but this also prevents the function from ever being inlined, and you have to refer to the object itself via pointer or reference for a virtual function call to work in the first place (otherwise you get "object slicing" which may have wider repercussions).

Usually, this is indeed not a very big cost, which is why some languages like Java can get away with things like "making all class variables references" and "making all methods virtual by default", presumably in the hopes of creating a simpler language for applications that can be developed without a detailed understanding of how each language feature is typically implemented.

Side Question: If "b" is simply pointing to an object, why does it even need a type?

If you really want to, you can use void*, which is effectively a pointer with no type. But you shouldn't normally do that because properly typed pointers add a modicum of type safety. And most of the time, you should be using smart pointers like std::unique_ptr which are even safer since they automagically deallocate the memory they point to when you're done with them.

  • 3
    Just looked up Object Slicing. That seems to have clarified a few things. – M-R May 21 '16 at 21:31
  • 1
    Imho, the cost of the feature (which as you write is mostly fewer possible optimizations) pales beyond the misfeature of adding ill-thought-out customization-points without contract. – Deduplicator May 26 '16 at 20:32
5

Java

With respect to Java, your premise is completely flawed: you never have to specify that a function is virtual--all functions are virtual by default. Java does provide final to specify that a method can't be overridden, which has a somewhat similar effect, but isn't really identical. To the extent that final means "not virtual", it's still done in the opposite direction--it's basically and "opt-out" rather than "opt-in" situation.

C++

One of the things Bjarne has always emphasized about how to design objects is establishing and maintaining invariants. Some of those invariants have to do with values, such as "this value must be between 1 and 100" or "this value must be between 2 and 3 times that value", etc.

It is equally important, however, to establish and maintain invariants with respect to an object's behavior. These invariants establish a framework within which specific parts can vary. Short of truly ugly programming, it allows the programmer to specify and enforce certain behaviors via the compiler's type checking mechanism.

For example, let's consider a C++ vector. The C++ standard guarantees that a C++ vector provides amortized constant growth. If we allowed a vector's growth function to be overridden, a class derived from vector could violate that constraint--but since it's (hypothetically) publicly derived from vector, it can be used any place a vector is needed, including code that may depend upon that constraint being enforced.

Making functions virtual is also oriented toward using inheritance. Bjarne has (ever so gently) pointed out for years that many programmers overuse inheritance. It's perfectly fine to design classes that don't need or use virtual functions at all1.

So no, this decision in C++ isn't entirely (or even primarily) to avoid the overhead of calling a virtual function. Mostly it's about basic design, and objects enforcing invariants. Many of those invariants are on an object's data, but some are also on an object's behavior. Given the importance of enforcing invariants, functions should be non-virtual by default, so the invariants are enforced by default, and behavior is open to modification only where intended.


1. For example, consider his 2003 interview with Bill Venners.

  • Last paragraph is insightful, kudos – Chris Beck May 26 '16 at 0:16
0

Surely "b" now points to the correct object so that when we call b.doSomething, we know which method needs to be called.

Surely not!

The key problem is if b points to or references a base class but in actuality is a derived class, it is the doSomething method defined in that derived class that should be called, rather than the than doSomething method the base class.

Some languages always carry that baggage around and make every function the equivalent of a C++ virtual function. C++ gives you the option of not having to carry that baggage around. The advantage of not carrying that baggage around for non-virtual methods is that the decision becomes a compile time decision. The compiler and linker know exactly what function should be called; the runtime cost is zero.

The overhead of deciding which function to call during runtime just doesn't matter if the function in question is called but a few times an hour. That runtime overhead piles up massively if the function is instead called tens of thousands of times per second (or even more).

0

Apart from performance etc. ...

Assume that your base class has a method doSomething. Assume that your derived class doesn't care about doSomething whatsoever. The developer has forgotten that it even exists. The developer of the derived class also wants to create a method that does something and call it doSomething, dosomething, do_something or something like that.

If the new method, which is totally unrelated to doSomething in the base class, is also called doSomething, then you are going to have a problem. If the language can do anything to avoid that problem, that would be great.

Or assume the other way round, the developer wants to override doSomething, but uses a wrong spelling for the name by mistake. Again, if the language can do anything to avoid that problem, that would be great.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.