What is Shadowing, and Is It True that C++ Does It?
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);}
};
Using these classes requires heap instances and -> instead of .:
A* a = new A();
a->f(1);
a->report();
a->f("Hello");
a->report();
B* b = new B();
b->nothing();
b->f(2);
b->report();
b->f("Yoo-Hoo!");
b->report();
b->f(3,"Oops");
b->report();
b->A::f(4);
b->report();
This main won't compile, just as in the unmanaged case, because the two-parameter overload of f() hides the original one-parameter versions. If you comment out the offending lines, you get exactly the same results as in the unmanaged code.
