Definition of Overriding in Owner Class
From Andrey
Consider a class A with a public virtual function f():
class A { public: virtual int f(); };
Class B contains a member a belonging to class A:
class B { A a; };
It is necessary to perform certain actions in B at the moment the user code calls method f() of its member B::a.
- The mechanism of overriding in the owner class is an extension of polymorphism allowing a virtual function of class A to be overridden in class B which includes an object of A as a member.
According to this definition, f() can be overridden in class B
class B { A a; int a::f(); };
so that the following is true:
a.f(); // B::a::f() is called, instead of A::f() … A& r = a; r.f(); // B::a::f() is called, instead of A::f()
Now every time the client calls f() on B::a, B is aware of it. B also has the possibility to adapt f()'s implementation to its own needs.
Example.
class size { long value; public: long get() { return value; } virtual void set(long l) { value == l; } }; class lee_jeans { size waist, inseam; void waist::set(long l) { assert(l > 28 && l <= 36 ); // call the original waist.size::set(l); } void inseam::set(long l) { assert(l > 30 && l <= 42 ); // call the original inseam.size::set(l); } };
This example shows how overriding in the owner can help a class to control the values assigned to its members.
Requirements to participating classes.
Overriding in owner has the following requirements to participating classes:
- The first participating class (class A) must declare a public virtual function.
- The second participating class (class B) must provide a function override and include an object of class A as a member.
- The declaration of the override associates the name of the overridden function with the name of the member for which the override is done.
Similarities with the usual overriring.
New overriding has the following in common with the standard overriding of C++:
- When entering the body of override, this points to an instance of the overriding class (B), not of the original one (A). However, in our case these classes are not related through the mechanism of inheritance.
- The override is called not only when the original function is called directly on the member, but also when the original function is called on a pointer to, or reference to the member object, a pointer to one of the member's ancestors in the inheritance chain, or a pointer to one of the member's base classes in the multiple inheritance – in other words, when the exact type of the member is not known to the caller.
- The function override is called instead of the original function, and therefore it must call the original function from its body, if it is not a full substitute. The call should be qualified with the name of the member’s class, to disable the virtual binding and prevent the override from getting called again. In addition, the call should be qualified with the name of the member itself (in our case, a.A::f(), not just A::f()), because an original function can only be called on an object belonging to the type of the member (A).
- A property of transitivity: a class can override not only the functions of its members, but also the functions of the members of its members. If we declare B::a::f() virtual and override it in the class C (suppose C can access methods of B):
class C { B b; int b::a::f(); };
then the following is true
b.a.f(); // C::b::a::f() is called b.a.B::a::f(); // B::a::f() is called b.a.A::f(); // A::f() is called
If f() is not overridden in the class B, expression
b.a.B::a::f();
is equivalent to b.a.A::f().
Differences with to the usual overriring.
Overriding in owner class is not simply the usual C++ overriding with the owner class playing the role of the derived class. There are some important differences:
- The overriding is done not for a class (A), but for a member of a class (B::a), that is, it does not affect any objects except the member object.
- Only a function belonging to the public interface of the member's class can be overridden.
- In the body of the function override, only the public interface of the member's class can be used. That is, overriding in owner class gives different access rights to its first participating class than the usual override.
- A class can use an abstract class for its member. In this case, one must use overriding in owner class to provide implementation for any pure virtual functions of the member’s class.
Note, that we only consider the overriding of methods of a contained member. Overriding at run time of methods of objects referred to by pointers initially looked very interesting. However, it turned out to be too complicated for debugging, because of its often unpredictable, extremely dynamic nature. I have left such applications outside of the scope of this article. In case of overriding for a contained member, the override declaration is close to the member, making it easy to understand the relationship. Besides, the implementations of the overriding mechanism can rely on the exact type of member object, known at compile time, instead of relying on the dynamic dependencies at run-time.
Overriding in owner class is not extending or modifying the interface of the owner class. Function overrides belong to the interface of the member, not to the interface of the owner. For instance, B::a::f() is part of the interface of B::a, not B. It cannot be called from an object of class B. Hence, the following calls are syntactically incorrect:
b::a::f(); c.C::b::a::f();
For the same reason, there is an additional rule:
- The access rights of the overrides should be the same as the access rights of the member for which they are done (overrides for a public member should be public, etc.).
An implementation of overriding in owner class in C++ is described in the “Inside the Overriding Mechanism” section later in this text. It is relatively straightforward: it does not require special data structures, does not interfere with other language features, and does not lead to losses in efficiency or memory. The same mechanism of dynamic binding through virtual method tables is used for overriding in owner class as for the usual overriding.