简体   繁体   中英

How virtual functions are called in c++

// b.h
#include <stdio.h>

class Tmp {
 public:
  // virtual void vfunc11() {}
  virtual void vfunc1();
  virtual ~Tmp() {}
};

class Tmp2 : public Tmp {
 public:
  virtual void vfunc1();
};

// b.cc
#include "tutorial/b.h"
#include <stdio.h>

void Tmp2::vfunc1() {
    printf("Tmp2::vfunc1\n");
}


// a.cc
#include "b.h"

int main() {
  Tmp *t = new Tmp2;
  t->vfunc1();
}

In the above example, the first time compile b.cc and a.cc and link them. At this time, t->vfunc1(); calls Tmp::vfunc1 .

Second uncomment virtual void vfunc11(int); , just recompile b.cc At this time, the link does not report an error, but the call is Tmp::vfunc11

I am gathering that you didn't recompile a.cc after changing the header. That is wrong.

With few exceptions that don't apply here, you must always recompile all translation units ( .cc / .cpp files) which include a header that you changed.

Otherwise your program will be broken and it will behave extremely unpredictably.

Technically you are causing undefined behavior, because you are linking two translation units, each with a different definition for Tmp , which is a one-definition-rule (ODR) violation.

If you declare a non-abstract function, it must be defined (implemented), even if it does nothing:

class Tmp {
 public:
  // virtual void vfunc11(int);
  virtual void vfunc1() {}
  virtual ~Tmp() {}
};

Otherwise you should make the function abstract:

class Tmp {
 public:
  // virtual void vfunc11(int);
  virtual void vfunc1() = 0;
  virtual ~Tmp() {}
};

So 'under the hood' each object of a class with virtual function holds data about how to call its version of that function.

That's a logical fact. During execution the code must have a way of determining which version of each virtual function to call for all the concrete object instances it may encounter(*).

In practice that's usually hidden data inside the object and normally a pointer to some data shared by all objects of the same concrete class. That shared data is usually called a 'v-table' because it's basically an array and the code accesses it by subscript. For a class with 3 virtual functions it will have 3 rows including the code entry point of the virtual functions. What you've done by a partial recompile is to cause code to access the wrong entry in the v-table. The code is calling virtual function subscript 0 and you've replaced it with a different function. Sneaky...

It's working because the two virtual functions ( vfunc1 and vfunc11 ) have same signature (parameters and return type - in this case void ) so when the code 'accidentally' calls the wrong function, everything happens to work(**).

I strongly recommend that unless you're a compiler implementer that is more than you need to know. Don't go down the rabbit hole of exactly how polymorphism is implemented unless you need to.

(*) It's worth pointing out that good optimisers can sometimes workout which function to call directly. If you instantiate an object as a local variable its concrete class is fixed so which virtual functions to call for it are fixed. Not all virtual function calls go through a lookup. But again, that is detail!

(**) Yes there totally is scope for hacking by conniving to get the wrong virtual function called so have your code malicious code called!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM