Hello!
I'm completely new to programming and have been learning C++ with the help of tutorials provided by Herbert Schildt(I'm using Visual Studio 2008). Recently I've come across virtual functions and below is a sample program from the book, which runs perfectly ok as it should printing: Base class, First derivation, Second derivation.
#include <iostream>
usingnamespace std;
class B {
public:
virtualvoid who() {cout << "Base class\n";}
};
class D1 : public B {
public:
void who() {cout << "First derivation\n";}
};
class D2 : public B {
public:
void who() {cout << "Second derivation\n";}
};
int main() {
B *p;
B B_ob; D1 D1_ob; D2 D2_ob;
p = &B_ob;
p->who(); p = &D1_ob;
p->who(); p = &D2_ob;
p->who();
return 0;
}
Then I tried to omit the virtual identifier and the program no longer functioned as it should printing: Base class, Base class, Base class. That proved that the virutal identifier is necessary.
But I experimented a bit and rewrote the program leaving out the virtual identifier and accessing member functions using the dot operator as shown below.
#include <iostream>
usingnamespace std;
class B {
public:
void who() {cout << "Base class\n";}
};
class D1 : public B {
public:
void who() {cout << "First derivation\n";}
};
class D2 : public B {
public:
void who() {cout << "Second derivation\n";}
};
int main() {
B B_ob; D1 D1_ob; D2 D2_ob;
B_ob.who();
D1_ob.who();
D2_ob.who();
return 0;
}
This time I expected an error to occur because I guessed that the compiler won't be able to choose between several functions identical in names, parameters and return types. Nonetheless it worked fine, just as it would with the use of virtual functions and a base class pointer. Why is that? Why use virtual if it "seemingly works without it" ? I'll be delighted if someone could explain me the difference between the two examples.
Because dynamic binding requires calling through pointers, not through objects. Your second snippet does work, but you don't have polymorphism. You can't make an array of B *, put both D1 * and D2 * into it, then call array[i]->who() and expect it to call the appropriate function. You'll be calling B::who(), which probably isn't what you need.
When not declared virtual, the who() methods in the derived classes hide the who()
method in the base class, so that calling who() on a derived object (D1_ob and D2_ob)
unambiguously refer to the derived class methods. In your first example, p is declared
to be a pointer to a base class instance (B). When compiling p->who(), the compiler
sees the declaration of B::who() as non-virtual and therefore calls that method. (The
compiler does not know at the call point that p is actually referring to derived class
instances; this is exactly what polymorphism is).
So if I get it right... When declared virtual, the compiler always thinks he's calling B's who() and it is decided during runtime, the method of which class to call. Basically one pointer(one interface?), several methods(several ways...)?
Thanks for helping me out, its not so easy to comprehend the principles of polymorphism.
When declared virtual, the compiler does not know which who() is to be called; rather it wll
get called via pointer (which you don't see). Research "virtual table" or "virtual method table"
for more details on how compilers implement polymorphism.
so your question is the difference between the two examples right?
first of all, class B, D1 and D2 has its own version of who() function. in the two examples D1 and D2 inherits the who() function of the base class B, so D1 and D2 has two versions of who() function.
in the first example when you tried to remove the virtual keyword, the output is always Base class. that is because instead of calling the corresponding who() function for each object (B::who(),
D1::who() and D2::who(), respectively), B::who() will be called in all cases since the
calls are via a pointer whose type is B*.
therefore, what the virtual keyword does is to allow a member of a derived class with the same name as one in
the base class to be appropriately called from a pointer, and more precisely when the type of the pointer is a
pointer to the base class but is pointing to an object of the derived class, as in the above example.
in the second example you are calling the corresponding who() function for each object since you are using the dot operator.
therefore, the dot operator is use when you are trying access the class's member who() function directly and not via pointer. but remember D1 and D2 has two version of who() function but in this case who() function inherited from the base class B is hidden because they have the same function name. to prove this, try to remove the who() function of D2 in both examples and the output should be: