Why worry about virtual functions in C ++?

This is not a question of how they work and are announced, this, in my opinion, is very clear to me. The question is, why should this be implemented? I believe the practical reason is to simplify a bunch of other code, to bind and declare their base type variables, handle objects and their specific methods from many other subclasses?

Can this be done using templates and type checking, for example, am I doing this in Objective-C? If so, which is more efficient? I am confused about declaring an object as one class and creating it as another, even if it is its child.

SOrry for stupid questions, but I have not done any real C ++ projects, but since I am an active Objective-C developer (it is a much smaller language that relies heavily on SDK functionality like OSX, iOS) I need to have clear presentation on any parallel paths of both cousins.

+7
source share
6 answers

Yes, this can be done using templates, but then the caller must know what the type of object is (a specific class), and this increases the connection.

Using virtual functions, the caller does not need to know the actual class - it works through a pointer to the base class, so you can compile the client once, and the developer can change the actual implementation as much as he wants, and the client does not need to know about it until the interface is changing.

+5
source

I don't know anything about Objective-C, but that’s why you want to "declare an object as one class and instantiate it as another": The Liskov replacement principle .

Since a PDF is a document, and an OpenOffice.org document is a document, and a Word document is a document, it’s natural to write

Document *d; if (ends_with(filename, ".pdf")) d = new PdfDocument(filename); else if (ends_with(filename, ".doc")) d = new WordDocument(filename); else // you get the point d->print(); 

Now, for this, print must be virtual or implemented using virtual functions, or implemented using an unreasonable hack that reinvents the virtual wheel. The program needs to know at run time which of the print methods to use.

Templating solves another problem when you determine at compile time which of the various containers you intend to use (for example) when you want to save a bunch of elements. If you work with these containers with template functions, you do not need to rewrite them when switching containers or adding another container to the program.

+1
source

Virtual functions realize polymorphism. I do not know Obj-C, so I can not compare both, but the motivating use case is that you can use derived objects instead of base objects, and the code will work. If you have a compiled and working foo function that works with a base reference, you don’t need to modify it to work with the derived instance.

You can do this (provided that you had information about the type of runtime) by getting the actual type of the argument and then sending the corresponding function directly using the short circuit switch, but for this you will need to either manually change the switch for each new one (high cost servicing) or the presence of reflection (not available in C ++) to get a method pointer. Even then, having received a method pointer, you will have to call it, which is as expensive as a virtual call.

As for the cost associated with a virtual call, basically (in all implementations with a table of virtual methods), a call to the virtual function foo , applied to the object o : o.foo() , translates to o.vptr[ 3 ]() , where 3 is the position of foo in the virtual table, and this is the compile-time constant. This is basically a double direction:

From the o object, get a pointer to a vtable, index this table to get a pointer to a function, and then call it. The extra cost compared to a direct non-polymorphic call is just a table search. (Actually, there may be other hidden costs when using multiple inheritance, since the implicit this pointer may be biased), but the cost of virtual sending is very small.

+1
source

In inheritance, a virtual function is important. Think of an example where you have the CMonster class, and then the CRaidBoss and CBoss class, which inherits from CMonster.

Both must be painted. CMonster has a Draw () function, but the way CRaidBoss and CBoss are drawn is different. Thus, the implementation remains for them using the virtual Draw function.

0
source

Well, the idea is to let the compiler do the checks for you.

It looks like a lot of features: ways to hide what you don't want to do yourself. This abstraction.

Inheritance, interfaces, etc. allow you to provide an interface to the compiler to match the implementation code.

If you did not have a virtual mecanism function, you need to write:

 class A { void do_something(); }; class B : public A { void do_something(); // this one "hide" the A::do_something(), it replace it. }; void DoSomething( A* object ) { // calling object->do_something will ALWAYS call A::do_something() // that not what you want if object is B... // so we have to check manually: B* b_object = dynamic_cast<B*>( object ); if( b_object != NULL ) // ok it ab object, call B::do_something(); { b_object->do_something() } else { object->do_something(); // that a A, call A::do_something(); } } 

There are several issues here:

  • you need to write this for every function overridden in the class hierarchy.
  • you have one more if for each child class.
  • you need to touch this function again every time you add a definition to the entire hierarchy.
  • this is visible code, you can easily make a mistake every time

So, the virtual marking functions do this correctly in an implicit way , automatically redirecting the function call for the correct implementation, depending on the final type of the object. You do not need to write any logic so that you cannot get errors in this code and have an extra thing to worry about.

This is something you don't want to worry about, as it can be done by the compiler / runtime.

0
source

The use of patterns is also technically known as the polymorphism of theorists. Yes, both are a valid approach to the problem. The implementation technique used will explain to them better or worse performance.

For example, Java implements patterns, but through erasing patterns. This means that he, apparently, uses only patterns, below the surface - a simple old polymorphism.

C ++ has very powerful templates. Using templates makes code faster, although each use of a template creates it for a given type. This means that if you use std :: vector for int, double and string, you will have three different vector classes: this means that the size of the executable will suffer.

0
source

All Articles