Why can I override a function without the virtual key word?

I am teaching myself virtual functions in C++. The code below compiles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <string>

using namespace std;

class Fish {
  string name;
  public:
    Fish(string);
    string getName();
    void doSwag() {cout << ":D" << endl;}
};

Fish::Fish(string name) {
  this->name = name;
}

string Fish::getName() {
  return this->name;
}

class Shark : public Fish {
  public:
    Shark(string);
    void eatPeople();
    void doSwag();
};

Shark::Shark(string name) : Fish(name) {}

void Shark::eatPeople() {
  cout << "NOM NOM NOM" << endl;
}

void Shark::doSwag() {
  cout << ":D :D :D" << endl;
}

int main() {
  Shark* f = new Shark("Sharky Larky");
  cout << f->getName() << endl;
  f->eatPeople();
  f->doSwag();
  delete f;
  return 0;
}


When I run it, the doSwag method of object f prints

:D :D :D

However, I never typed the term virtual before void doSwag in the Fish class.

Shouldn't doSwag print :D instead of :D :D :D then?
No, you have a pointer to a `Shark'.
1
2
3
4
5
6
7
8
void foo(Fish &wanda){
   wanda.doSwag();
}

int main(){
   Shark topus("Larry");
   foo(topus);
}
Also, you don't need to use dynamic allocation in order to observe polymorphism.
Last edited on
Shark topus


Bwahahahahaahahahaha

EDIT: the fish called wanda was a nice touch, too.

A+, ne555. A+.
Last edited on
Thank you! But why does me having a pointer to a `Shark` as opposed to a `Fish` allow me to override the subclass method `doSwag` without a virtual key word? Shouldn't the compiler warn me about that or issue an error?

And true, thanks, I could store the object on the stack instead of the heap to discourage memory leaks.
There's no warning/error here. This is typical behavior.

Virtual functions examine the actual object type at runtime and call the appropriate function based on that.

Non-virtual functions simply look at the "apparent" type at compile time and call the appropriate function.

That said... here's an example of what I mean:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Fish
{
public:
  virtual void Virt() { ... }
  void NonVirt() { ... }
};

class Shark : public Fish
{
public:
  virtual void Virt() { ... }
  void NonVirt() { ... }
};


Here we have a parent class 'Fish' and a child class 'Shark'.
Each class has 2 member functions, one virtual and one non-virtual
This means we have a total of 4 separate functions:

1) Fish::Virt
2) Fish::NonVirt
3) Shark::Virt
4) Shark::NonVirt

Which of these functions gets called depends on the type of object we're acting on. Continuing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int main()
{
  Shark topus;  // topus is a Shark

  Shark* sh = &topus;  // sh is a pointer to a shark.  In this case, it points to topus.

  // now let's call each function and see what happens:
  sh->NonVirt(); // The non virtual function call.  We are acting on 'sh' which is of type
   // Shark*.  The compiler sees this, and so it calls the shark form of the function.
   // therefore this calls function #4: Shark::NonVirt

  sh->Virt();  // the virtual function call.  This does not look at the type of 'sh', but rather
   // it looks at the type that sh points to (ie:  the "actual" type, not the perceived type).
   // Here, since 'sh' points to 'topus', and 'topus' is of type 'Shark', this also calls the Shark
   // form.  Therefore this calls function #3:  Shark::Virt


  // now let's get tricky...

  Fish* wanda = &topus;  // wanda is a pointer to a fish.  In this case, it points to topus
    // note that topus is actually a Shark, but it's also a Fish because Shark derives from Fish

  wanda->NonVirt();  // The non-virtual call.  The compiler examines the type of 'wanda',
    // which is of type 'Fish*'.  Therefore this calls the Fish form of the function:
    // function #2:  Fish::NonVirt


  wanda->Virt();  // The virtual call.  This is where the virtual keyword is so useful.
    // rather than just examining the type of 'wanda'... this actually will look at the object
    // wanda points to in order to determine the actual type.  Here, wanda points to topus.
    //  and we know that topus is ACTUALLY a Shark.  Therefore this calls the Shark form
    //  instead of the Fish form.  Therefore this calls function #3:  Shark::Virt
}
Topic archived. No new replies allowed.