Dynamic binding of FP-15 messages

Posted by Edd on Thu, 03 Mar 2022 23:18:07 +0100

Structure 15 dynamic binding of messages

Message polymorphism

Message polymorphism is reflected in that the same message can be sent to different types of objects, so it will get different interpretations.

For two classes with public inheritance relationship, one message can be sent to the base class object or to the derived class object. If the processing of this message is given in both the base class and the derived class, the message is polymorphic.

Since the pointer or reference of the base class can point to or reference the base class object, it can also point to or reference the derived class object:

  • When a message is sent to the object it points to or references through the pointer or reference of the base class, there is a binding problem for the message (member function call).

  • C + + uses static message binding by default: message processing using base classes.

class A
{	 int x,y;
  public:
	 void f();
};
class B: public A
{	 int z;
  public:
   	void f(); 
   	void g();
};
void func1(A& x)
{	......
	x.f(); //Call A::f
	......
}
void func2(A *p)
{	......
	p->f(); //Call A::f
	......
}
......
A a;
func1(a);
func2(&a);
B b;
func1(b);
func2(&b);

Dynamic binding of messages

  • In general, you need to decide whether to call A::f or B::f in func1 (or func2) according to the object actually referenced (or pointed to) by x (or p), that is, dynamic binding is adopted.

  • In C + +, virtual functions are used in base classes to indicate dynamic binding.

class A
{		int x,y;
	public:
		virtual void f(); //virtual function
};
class B: public A
{		int z;
	public:
   		void f(); 
   		void g();
};
void func1(A& x)
{	......
	x.f(); //Call A::f or B::f
	......
}
void func2(A *p)
{	......
	p->f(); //Call A::f or B::f
	......
}
......
A a;
func1(a); //Calling A::f in func1
func2(&a); //Calling A::f in func2
B b;
func1(b); //Calling B::f in func1
func2(&b); //Calling B::f in func2

virtual function

Virtual function refers to the member function with the keyword virtual. The format is:

virtual <Member function declaration>;

Virtual functions have two functions:

  • The specified message adopts dynamic binding.

  • Indicates the member functions in the base class that can be redefined by the derived class.

Redefine the member function of the base class in the derived class

For a virtual function in the base class, the member function with the same structure defined in the derived class is the redefinition (or override) of the member function of the base class.

The same configuration refers to:

  • The name, number of parameters and type of the member function defined in the derived class are the same as those of the corresponding member function of the base class;

  • Its return value type is either the same as the return value type of the base class member function, or it is a public derived class of the return value type of the base class member function.

class A
{	public:
      A f();
      void g();
};
class B: public A
{	public:
	  A f(); //Newly defined member (not related to f of A, but with the same name!)
	  void f(int); //Newly defined members
     void h(); //Newly defined members
};
class A
{	public:
      virtual A f();
      void g();
};
class B: public A
{	public:
	  A f(); //Redefinition of member f in class A. The return type can also be B.
	  void f(int); //Newly defined members
     void h(); //Newly defined members.
};

Some notes on virtual functions

Only member functions of a class can be virtual functions, but static member functions cannot be virtual functions.

Constructors cannot be virtual functions, and destructors can (often) be virtual functions.

As long as the virtual function is described in the base class, in the derived class, the derived class of the derived class,..., the member functions of the same structure are virtual functions (virtual can not be written).

Dynamic binding occurs only when a virtual function of a class is accessed through a pointer or reference.

Class constructors and destructors do not dynamically bind calls to virtual functions.

Various situations of message dynamic binding

class A
{  ......
  public:
	A() { f(); }
	~A() { f(); }
	virtual void f();
	void g();
	void h() { f(); g(); }
};
class B: public A
{  .......
 public:
   B() { ...... }
	~B();
	void f(); 
	void g(); 
};
......
A a;  //Call A::A() and A::f
a.f();  //Call A::f
a.g();  //Call A::g
a.h();  //Call A::h, A::f, and A::g
//When a dies, A::~A() and A::f will be called

B b;  //Call B::B(), A::A(), and A::f
b.f();  //Call B::f
b.g();  //Call B::g
b.h();  //Call A::h, B::f and A::g
//When b dies, B::~B(), A::~A() and A::f will be called
A *p;   //p is a Class A (base class) pointer
p = &a; //p points to class A objects
p->f();  //Call A::f
p->g();  //Call A::g
p->h();  //Call A::h, A::f and A::g
p = &b;  //p points to class B objects
p->f();  //Call B::f
p->A::f(); //Call A::f
p->g();  //Call A::g, and the non virtual function adopts static binding
p->h();  //Call A::h, B::f and A::g
p = new B;  //Call B::B(), A::A() and A::f
.......
delete p;  //Only call A::~A() and A::f,
               //B:~B() is not called. Why?
                 //The destructor of A is not defined as A virtual function!

Access the newly defined members in the derived class through the base class pointer

class A
{	      .......
   public:
		virtual void f();
};
class B: public A
{	      ......
    public:
		void f(); 
		void g(); 
};
A *p=new B;
......
p->f(); //OK
p->g(); //Error
((B *)p)->g(); //OK, not safe! p may not point to class B objects!
//Safety practices
B *q=dynamic_cast<B *>(p);
if (q != NULL) q->g();
//Runtime type information (RTTI) support is required

When do I need to define a virtual function?

Which member functions in the base class need to be designed as virtual functions?

  • When designing the base class, although the implementation of some member functions is sometimes given, the implementation method may not be the best, and there may be better implementation methods in the future.

  • The implementation of some member functions cannot be given in the base class at all. They must be given by different derived classes according to the actual situation. (pure virtual function)

Implementation of virtual function dynamic binding

class A
{	int x,y;
   public:
	virtual void f();
	virtual void g();
	void h();
};
class B: public A
{	int z;
   public:
	void f();
	void h();
};

void f(A *p) //Can receive class A and B objects
{ p->h(); //Compiled into: A::h(p);
   p->f();  //Compiled into: (* (* (VtblPtr*)p))(p);
   p->g(); //Compiled into: (* (* (VtblPtr*)p+1))(p);
}

Topics: C++ OOP inheritance