What are the advantages of using the dynamic link objetoPtr->funcion();
on the static link objeto.funcion();
?
And why is it good to declare functions as virtual?
What are the advantages of using the dynamic link objetoPtr->funcion();
on the static link objeto.funcion();
?
And why is it good to declare functions as virtual?
In C ++ the polymorphism has nothing to do with the way you access the members of an object (either with the dot operator .
or the operator arrow ->
). Broadly speaking the polymorphism would have some similarities to the polisemy of oral / written language.
That is, we have an object of type Base
that according to how it has been initialized will behave as Derivado_1
or Derivado_2
in the same way as the word" capital " (and other polysemous) can mean different things according to their context.
Regarding whether the polymorphism is static or dynamic, I'll let this man answer:
Translation:polymorphism - providing a single interface to entities of different types. virtual functions provide dynamic (run-time) polymorphism through an interface provided by a base class. Overloaded functions and templates provide static (compile-time) polymorphism .
polymorphism - provide a unique interface to entities of different types. Virtual functions provide dynamic polymorphism (execution time) through an inetrface provided by a class. Overloaded functions and templates provide static polymorphism (compile time).
Once clarified that the polymorphism has nothing to do with the way in which the members of an object are accessed, we will see the types of polymorphism mentioned by Bjarne .
Imagine that we have two functions with the same name but different parameters:
void funcion(int) { std::cout << "Entero\n"; }
void funcion(unsigned int) { std::cout << "Entero sin signo\n"; }
The función
function would be polymorphic since it would take differently depending on how it is called:
funcion(0123); // Muestra "Entero"
funcion(0xcafeu); // Muestra "Entero sin signo"
This effect also occurs with the templates, with the difference that we write the body of the function only once and the compiler will instantiate it for us once for each type 1 :
template <typename T>
void funcion(T) { std::cout << __PRETTY_FUNCITON__ << '\n'; }
int main(int, char **argv)
{
funcion(0b0000'0000'0010'1010); // Muestra "void funcion(T) [with T = int]"
funcion(u8"Tres tristes tigres"); // Muestra "void funcion(T) [with T = const char*]"
funcion(L"Comían trigo en un trigal"); // Muestra "void funcion(T) [with T = const wchar_t*]"
funcion(argv); // Muestra "void funcion(T) [with T = char**]"
return 0;
}
Obviously, this is also true for member functions:
struct S
{
void f(float) { std::cout << "Flotante\n"; }
void f(double) { std::cout << "Doble\n"; }
};
S s, *t = new C;
s.f(1.f); // Muestra "Flotante"
s.f(2.); // Muestra "Doble"
t->f(3.f); // Muestra "Flotante"
t->f(4.); // Muestra "Doble"
These are examples of static polymorphism, since the decision of the form that the function should take is decided statically (at compile time).
Imagine that we have three objects, deriving the last two from the first:
struct Base { void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
struct Derivado_1 : public Base { void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
struct Derivado_2 : public Base { void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
By using these objects we have the following result 1 :
Base b;
Derivado_1 d1;
Derivado_2 d2;
b.f(); // Muestra "void Base::f()"
d1.f(); // Muestra "void Derivado_1::f()"
d2.f(); // Muestra "void Derivado_2::f()"
If we use a pointer to Base
to refer to any of the derived objects we have the following result 1 :
Base b;
Derivado_1 d1;
Derivado_2 d2;
Base *b1 = &d1, *b2 = &d2;
b1->f(); // Muestra "void Base::f()"
b2->f(); // Muestra "void Base::f()"
Although the pointers b1
and b2
contain objects of type Derivado_1
and Derivado_2
respectively, the function Base::f
is being called; this should not be surprising since, after all, the pointer type is Base
; if we want to call the function f
of the derivatives we must mark the function f
of the base class as virtual (this answers one of your questions):
struct Base { virtual void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
// ^^^^^^^
struct Derivado_1 : public Base { void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
struct Derivado_2 : public Base { void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
Base b;
Derivado_1 d1;
Derivado_2 d2;
Base *b1 = &d1, *b2 = &d2, *pb = &b;
b1->f(); // Muestra "virtual void Derivado_1::f()"
b2->f(); // Muestra "virtual void Derivado_2::f()"
pb->f(); // Muestra "virtual void Base::f()"
Note that it has not been necessary to mark as virtual functions f
of the derivatives so that these are considered as virtual by the compiler ... they have " contagious " of the virtuality to the override the function f
of Base
marked as virtual
.
These are examples of dynamic polymorphism, since the decision of the form that the function must take is decided dynamically (at run time).
There are many more things to know about polymorphism, such as purely virutal functions, static polymorphism of classes or the qualifier override
, but for other questions, I encourage you to write new questions about these topics (after having investigated a little on your own, of course).
1 The output is generated with GCC, other compilers may not have __PRETTY_FUNCTION__
.