1 Overload and overwrite
Features of member functions being overloaded:
(1) The same scope (in the same class);
(2) The function name is the same;
(3) Different parameters;
(4) The virtual keyword is optional.
Overwrite refers to the derived class function covering the base class function, and the characteristics are:
(1) Different ranges (located in derived classes and base classes respectively);
(2) The function name is the same;
(3) The parameters are the same;
(4) The base class function must have virtual keywords.
In the following example, the functions Base::f(int) and Base::f(float) are overloaded with each other, while Base::g(void) is overwritten by Derived::g(void).
#include <>
class Base
{
public:
void f(int x){ cout << "Base::f(int) " << x << endl; }
void f(float x){ cout << "Base::f(float) " << x << endl; }
virtual void g(void){ cout << "Base::g(void)" << endl;}
};
class Derived : public Base
{
public:
virtual void g(void){ cout << "Derived::g(void)" << endl;}
};
void main(void)
{
Derived d;
Base *pb = &d;
pb->f(42); // Base::f(int) 42
pb->f(3.14f); // Base::f(float) 3.14
pb->g(); // Derived::g(void)
}
2 Confusing hidden rules
It was not difficult to just distinguish overloading from overriding, but the hidden rules of C++ suddenly increased the complexity of the problem.
Here "hidden" means that the function of the derived class blocks the base class function with the same name as it. The rules are as follows:
(1) If the function of the derived class has the same name as the function of the base class, but the parameters are different. At this time, regardless of whether there are or not virtual keywords, the functions of the base class will be hidden (note that they do not be confused with overloading).
(2) If the function of the derived class has the same name as the function of the base class and the parameters are the same, but the base class function does not have a virtual keyword. At this time, the functions of the base class are hidden (note not to be confused with overrides).
In the following example:
(1) The function Derived::f(float) overrides Base::f(float).
(2) The function Derived::g(int) hides Base::g(float) instead of overloading.
(3) The function Derived::h(float) hides Base::h(float) instead of overwriting.
#include <>
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
According to the author's investigation, many C++ programmers do not realize that there is "hidden". Due to the lack of deep understanding, the occurrence of "hidden" can be said to be elusive and often produces confusing results.
In the following example, bp and dp point to the same address. The operation results should be the same, but this is not the case.
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
3 Get rid of hiding
Hiding the rules has caused a lot of trouble. In the following example, the original meaning of the statement pd->f(10) is to call the function Base::f(int), but Base::f(int) is unfortunately hidden by Derived::f(char *). Since the number 10 cannot be implicitly converted into a string, an error occurred at compile time.
class Base
{
public:
void f(int x);
};
class Derived : public Base
{
public:
void f(char *str);
};
void Test(void)
{
Derived *pd = new Derived;
pd->f(10); // error
}