Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Function overriding in C++/CLI

0.00/5 (No votes)
22 Jun 2004 3  
Takes a look at new features like explicit overriding, renamed overriding, multiple overriding and sealing a method

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:
    //Overrides Base::Goo

    virtual void Goo()
    {
        Show("Derived::Goo");
    }
    
    //Overrides Base::Boo as above

    virtual void Boo() = Base::Boo
    {
        Show("Derived::Boo");
    }
    
    //Hides Base::Doo

    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:
    //Overrides Base::Goo

    virtual void Goo()
    {
        Show("Derived::Goo");
    }

    //Overrides Base::Boo

    virtual void Woo() = Base::Boo
    {
        Show("Derived::Woo");
    }

    //New function Boo in Derived

    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
} // end of method Derived::Boo

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:
    //...


    //Override Base::Boo

    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:    
    //Overrides both Base::Goo, INish::Goo, IBuster::Moo

    virtual void Goo() = Base::Goo, INish::Goo, IBuster::Moo
    {
        Show("Derived::Goo");
    }

    //Overrides Base::Boo

    virtual void Boo() = Base::Boo
    {
        Show("Derived::Boo");
    }

    //Override IBuster::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() //won't compile

    {
        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 :-)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here