Introduction
This article will demonstrate some of the new features provided by C++/CLI
in connection with function overloading. Since, at the time of writing this
article, the author only has access to an alpha version of the compiler, some of
the code snippet syntax shown here might change in the final release, which is
expected to be post-June 2005. Of course, the article will be updated whenever
the author gets a newer version of the compiler where the syntax is slightly
different from how it's portrayed in this article. Readers are expected to know
basic C++/CLI syntax, and if they don't, the author strongly recommends that
they take a look at one of the author's own articles -
A first look at C++/CLI; which gives a basic introduction to the new C++/CLI
syntax introduced by Microsoft, starting with VC++ Whidbey.
Explicit overriding
In native C++, a derived class function having the same name and parameters
as a base class virtual
function will *always* override
it. In C++/CLI you have the option of using the new
contextual keyword to specify whether you want to override a base class function
or hide it.
BTW I have used the following #define
as I didn't
want to type in (or even copy/paste) Console::WriteLine
all the time.
#define Show(x) Console::WriteLine(x)
The following code snippet demonstrates the use of the new
contextual keyword.
ref class Base
{
public:
virtual void Goo()
{
Show("Base::Goo");
}
virtual void Boo()
{
Show("Base::Boo");
}
virtual void Doo()
{
Show("Base::Doo");
}
};
ref class Derived : Base
{
public:
virtual void Goo()
{
Show("Derived::Goo");
}
virtual void Boo() = Base::Boo
{
Show("Derived::Boo");
}
virtual void Doo() new
{
Show("Derived::Doo");
}
};
Here's some sample code that invokes the above methods on a
Base
handle referencing a Derived
object.
void _tmain()
{
Base^ r = gcnew Derived();
r->Goo();
r->Boo();
r->Doo();
}
You'll get the following output :-
Derived::Goo
Derived::Boo
Base::Doo
Let's look at part of the IL generated for each of these methods :-
IL for Derived::Goo
.method public virtual instance void Goo() cil managed
{
.maxstack 1
IL_000a: ret
}
Okay, nothing peculiar about that code, everything seems normal
:-)
IL for Derived::Boo
.method public newslot virtual final instance void
Boo() cil managed
{
.override Base::Boo
.maxstack 1
IL_000a: ret
}
Hmmm, notice the .override
IL directive that has
been generated, which specifies that a new virtual
method should be generated using the same signature as in the base class, but
with a different name, and it's this name that is specified by the
.override
directive. Of course, it does not really affect the result in our above
case because it again simply mentions the base class's method-name which is same
as the derived class's method-name. Later, when we look at renamed overriding,
we'll see a more appropriate use of the .override
directive.
IL for Derived::Doo
.method public newslot virtual instance void
Doo() cil managed
{
.maxstack 1
IL_000a: ret
}
Notice the newslot
IL keyword that has been used in
the method definition. If any method definition is marked as
newslot
then it always creates a new virtual
method, even if its base class defines a virtual
method with the same name and
parameter list.
Renamed overriding
Native C++ insisted that the derived class method name must match the name of
the base class virtual
method that it is overriding.
C++/CLI allows us to have a derived class method override a base class
virtual
method even if the derived class method name does not match the
base class method name. Of course the method signatures must be equivalent.
The following code snippet should make things clear.
ref class Base
{
public:
virtual void Goo()
{
Show("Base::Goo");
}
virtual void Boo()
{
Show("Base::Boo");
}
};
ref class Derived : Base
{
public:
virtual void Goo()
{
Show("Derived::Goo");
}
virtual void Woo() = Base::Boo
{
Show("Derived::Woo");
}
virtual void Boo() new
{
Show("Derived::Boo");
}
};
Some code that invokes these methods :-
void _tmain()
{
Base^ r = gcnew Derived();
r->Goo();
r->Boo();
Derived^ d = dynamic_cast<Derived^>(r);
d->Goo();
d->Boo();
d->Woo();
}
The output will be :-
Derived::Goo
Derived::Woo
Derived::Goo
Derived::Boo
Derived::Woo
Let's look at the IL for Derived::Woo
and Derived::Boo
.
IL for Derived::Woo
.method public newslot virtual final instance void
Woo() cil managed
{
.override Base::Boo
.maxstack 1
IL_000a: ret
}
As mentioned earlier, the IL .override
directive allows
us to have a derived class method generated, that will override a base class
virtual
method of a different name but the same
signature. You'll also notice that the newslot
keyword
has been added to the method definition so that a new virtual
method will be generated. In this way, even if the base class had a method of
the same name, the derived class method will actually be a new
virtual
method that's overriding a base class virtual
method different from the one with the same name.
IL for Derived::Boo
.method public newslot virtual instance void
Boo() cil managed
{
.maxstack 1
IL_000a: ret
}
If we hadn't specified Derived::Boo
as a new
method, we'd have got a compiler
error, as Base::Boo
has been overridden by Derived::Woo
:-
error C3663: 'Derived::Boo' :
method implicitly overrides 'Base::Boo'
which has already been explicitly overridden
But since we have specified that this function is a new virtual
function, the generated IL uses the IL newslot
keyword
on the method definition.
Alternative syntax
A slightly cleaner looking syntactic alternative is to use the contextual
identifier override
as shown below :-
ref class Derived : Base
{
public:
virtual void Woo() override = Base::Boo
{
Show("Derived::Woo");
}
};
Multiple overriding
In native C++, a function of a class that inherits from multiple base
classes/interfaces, can override more than one base class function only if all
the base classes/interfaces have a function of the same name and signature. C++/CLI
lets you specify which method overrides which base/ class/interface method,
provided the function signatures match.
The following code snippet demonstrates multiple overriding :-
interface class INish
{
void Goo();
};
interface class IBuster
{
void Boo();
void Moo();
};
ref class Base
{
public:
virtual void Goo()
{
Show("Base::Goo");
}
virtual void Boo()
{
Show("Base::Boo");
}
};
ref class Derived : Base, INish, IBuster
{
public:
virtual void Goo() = Base::Goo, INish::Goo, IBuster::Moo
{
Show("Derived::Goo");
}
virtual void Boo() = Base::Boo
{
Show("Derived::Boo");
}
virtual void Hoo()= IBuster::Boo
{
Show("Derived::Hoo");
}
};
Use the below code snippet to invoke these methods :-
void _tmain()
{
Base^ r = gcnew Derived();
r->Goo();
r->Boo();
INish^ i = dynamic_cast<INish^>(r);
i->Goo();
IBuster^ b = dynamic_cast<IBuster^>(r);
b->Boo();
b->Moo();
}
The output will be :-
Derived::Goo
Derived::Boo
Derived::Goo
Derived::Hoo
Derived::Goo
Let's see how the generated IL looks like.
IL for Derived::Goo
.method public newslot virtual final instance void
Goo() cil managed
{
.override IBuster::Moo
.override INish::Goo
.override Base::Goo
.maxstack 1
IL_000a: ret
}
Alright, that's not a biggie, is it? All that happens is that the
.override
IL directive is used multiple times, each
time specifying a different base class or interface.
Sealing a function from further overriding
Sometimes we might desire that a function should not be overridden somewhere
down the inheritance chain. That's where the sealed
function modifier comes handy.
ref class Base
{
public:
virtual void Goo() sealed
{
Show("Base::Goo");
}
};
ref class Derived : Base
{
public:
virtual void Goo()
{
Show("Derived::Goo");
}
};
The compiler will throw up an error :-
error C3248:
'Base::Goo': function declared as 'sealed'
cannot be overridden by 'Derived::Goo'
Of course you can get it to compile by using the new
function-modifier.
ref class Base
{
public:
virtual void Goo() sealed
{
Show("Base::Goo");
}
};
ref class Derived : Base
{
public:
virtual void Goo() new
{
Show("Derived::Goo");
}
};
But if you do this, then the following code snippet :-
void _tmain()
{
Base^ r = gcnew Derived();
r->Goo();
}
...will give the following output :-
Base::Goo
...because Derived::Goo
is now a new function and the generated
IL will have the newslot
keyword applied to the method
definition.
The abstract
function-modifier
ref class Base
{
public:
virtual void Goo() abstract;
virtual void Boo()
{
Show("Base::Boo");
}
};
The line virtual void Goo() abstract;
is
syntactically equivalent to virtual void Goo() = 0;
and
the abstract
function-modifier exists only to provide
an uniform syntactical style.
Conclusion
As mentioned in the introduction, this article is based on my experiences
with the latest version of the alpha compiler I could lay my hands on. Some of
the syntax demonstrated in the article might change by the time VC++.NET Whidbey
is finally released. But however that might turn out to be, I must say I am
quite fairly impressed by the awesome changes that the VC++ team is bringing to
the compiler. Thanks guys :-)