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.
Page 1 of 2
This article was originally published on February 27, 2004