What is Shadowing, and Is It True that C++ Does It?

Here’s a little Object-Oriented question: If I have a function f in my base class, say that takes an integer, and I write an overload of that function in a derived class that takes something different, can I still call the original function that was inherited from the base class? The answer might surprise you.

A Simple Base Class

Let’s start with some plain vanilla C++, no CLR in sight. Here’s a base class:

class A
{
protected:
   int i;
   char* s;
public:
   A():s(NULL),i(-1) {}
   void f(int ii) {i=ii;s=new char(0);}
   void f(char* ss) {i=0; if (s) delete s; s=
        new char[strlen(ss)];strcpy(s,ss);}
   void report () { cout << i << " " << s << endl;}
};

While it isn’t likely to win any contest for usefulness, and the second overload of f is a bit long for inlining, it will do. Clearly, you can use this class like this:

A a;
a.f(1);
a.report();
a.f("Hello");
a.report();

Deriving and Overloading

What happens to these overloads of f when I write a base class, B?

class B: public A
{
public:
    void nothing() {;}
    void f(int ii, char* ss){i=ii; if (s) delete s; s=
         new char[strlen(ss)];strcpy(s,ss);}
};

The answer, and this surprises many people, is that they seem to disappear. Here’s some calling code:

B b;
b.nothing();
b.f(2);
b.report();
b.f("Yoo-Hoo!");
b.report();

This code compiles only when B::f(int, char*) is commented out. With that overload in place, this code produces messages such as 'B::f' : function does not take 1 arguments. You can call only the two-parameter version of f():

b.f(3,"Oops");
b.report();

Of course, if you absolutely need to get to the base class function, you can, but you have to be explicit about it:

b.A::f(4);
b.report();

Managed Classes

What happens if A and B become managed classes in a managed console application? (For simplicity, let’s keep working with int and char*, but I will replace the iostream with System::Console equivalents.)

using namespace System;
__gc class A
{
protected:
   int i;
   char* s;
public:
   A():s(NULL),i(-1) {}
   void f(int ii) {i=ii;s=new char(0);}
   void f(char* ss) {i=0; if (s) delete s; s=
        new char[strlen(ss)];strcpy(s,ss);}
   void report () { Console::Write(__box(i));
                    Console::Write(" ");
                    Console::WriteLine(s);}
};

__gc class B: public A
{
public:
   void nothing() {;}
   void f(int ii, char* ss){i=ii; if (s) delete s; s=
        new char[strlen(ss)];strcpy(s,ss);}
};

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read