Managed C++/CLI Programming: Part 2

Before reading this article, I highly recommend reading the previous part:

Abstract

This part outlines the rest of C++/CLI Object Oriented Programming features such as inheritance, interface and polymorphism. We will learn the various control statements such as if, while and do-while constructs as well as other diverse looping constructs in detail such as for loop and switch constructs by applying C++/CLI semantics under the CLR execution model. Apart from that, we would be confronted with other significant notions such as exception handling, memory management, delegates and generics. Finally, this article illustrates how to mix the implementation of native C++ code with managed C++/CLI code under the CLR context.

Control Statements

Control statements define what code should executed from given statements. C++/CLI proposed if/else, conditional operator and Switch construct as control statements. The if/else construct syntax is very similar to C# coding as in the following.
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. int main(array<System::String ^> ^args)  
  5. {  
  6.     wchar_t ltr;  
  7.     Console::WriteLine("Enter the Letter");  
  8.     ltr= Console::Read();  
  9.     if (ltr >='a')  
  10.         if(ltr<='z')  
  11.         {  
  12.             Console::WriteLine("you have entered small Letter");  
  13.         }  
  14.     if (ltr >='A')  
  15.         if(ltr<='Z')  
  16.         {  
  17.             Console::WriteLine("you have entered capital Letter");  
  18.         }  
  19.     return 0;  
  20. }  
The conditional operator in C++/CLI is known as a ternary operator. The first argument must be a Boolean result; if the result is true, the first expression is evaluated; otherwise, the second one is, as in the following:
  1. String^ str= i>5 ? "India" : "USA";  
The switch construct is very similar to C# but with the difference that C++/CLI does not support a string in the case selection. Instead we need to use an if/else construct. The following is a simple sample of this construct:
  1. wchar_t days;  
  2.     Console::WriteLine("1 = Sunday");  
  3.     Console::WriteLine("2 = Monday");  
  4.     Console::WriteLine("3 = Tuesday");  
  5.   
  6.     Console::WriteLine("Enter your choice");  
  7.     days= Console::Read();  
  8.     switch(days)  
  9.     {  
  10.     case '1': Console::WriteLine("Sunday");  
  11.         break;  
  12.        case '2': Console::WriteLine("Monday");  
  13.         break;  
  14.     case '3': Console::WriteLine("Tuesday");  
  15.         break;  
  16.     default: Console::WriteLine("Out of Reach");  
  17.         break;  
  18.     }  
Loop Construct

C++/CLI defines for, for each, while and do-while loop constructs. With loops, the code is repeatedly executed until a condition is met. The for, while and do-while constructs are syntactically similar to C# as in the following:
  1. //for loop  
  2.       for(int i=0;i<5;i++)  
  3.     {  
  4.         //statements  
  5.     }  
  6. //while loop  
  7.     int x=0;  
  8.     while(x<3)  
  9.     {  
  10.         //statements  
  11.     }  
  12. //do-while loop  
  13.     do  
  14.     {  
  15.         //statements  
  16.     }while(i<3);  
The for each loop is in C++/CLI. It doesn't exist with ANSI C++ because it requires the IEnumerable interface.
  1. array<int>^ arry= {1,2,3,4,5};  
  2.   
  3. foreach(int x in arry)  
  4. {  
  5.   Console::WriteLine(x);  
  6. }  
Arrays

The C++/CLI introduced an array keyword to implement Arrays. This keyword uses a generic like syntax with angle brackets. The angle brackets are used to define the type of elements. C++/CLI supports array initializers with the same syntax as C#.
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. int main(array<System::String ^> ^args)  
  5. {  
  6.     //Array Declaration  
  7.     array<int>^ a1={ 10,20,30,40,50 };  
  8.   
  9.     for each(int i in a1)  
  10.     {  
  11.          Console::WriteLine(i);  
  12.     }  
  13.     Console::ReadLine();  
  14.     return 0;  
  15. }  
Static Members

The static members can be defined by the static keyword much like C#. A static field is instantiated only once for all objects of the type. We don't need to instantiate the class to access the static members. Instead we can directly access them using the class type name followed by "::" operator as (as in C# using the "." operator).
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. public ref class test  
  5. {  
  6. public:  
  7.     static int i;  
  8.     test()  
  9.     {  
  10.         i++;  
  11.         Console::WriteLine("Constructor Called :{0}",i);  
  12.     }  
  13. };  
  14.   
  15. int main(array<System::String ^> ^args)  
  16. {  
  17.     test^ obj=gcnew test();  
  18.     test^ obj1=gcnew test();  
  19.     //directly access of static member  
  20.     Console::WriteLine(test::i);  
  21.     Console::Read();  
  22.    return 0;  
  23. }  
Interface

The interface keyword is used to define an interface. Defining an interface in C++/CLI is similar to the C# language but the implementation is slightly different. The method that is defined in the interface must be implemented with virtual keyword in the child class as in the following:
  1. public interface class IDisplay  
  2. {  
  3.     void hello();  
  4. };  
  5.   
  6. public ref class test: IDisplay  
  7. {  
  8. public:  
  9.     virtual void hello()  
  10.     {  
  11.         Console::WriteLine("Hello test");  
  12.     }  
  13. };  
Inheritance

Inheritance is a mechanism in which base class members can be accessed in its corresponding derived class. All the C++/CLI classes are derived classes by default. This is because both value and reference classes have a standard base class System::Object. The base class should by followed by a colon (:) in the derived class as in the following:
  1. public ref class baseClass   
  2. {  
  3. public:  
  4.     virtual void showBase()   
  5.     {  
  6.         Console::WriteLine("base class");  
  7.     }  
  8. };  
  9. public ref class test : baseClass  
  10. {  
  11. public:  
  12.      void showDerived()   
  13.      {  
  14.         Console::WriteLine("derieved class");  
  15.      }  
  16. };  
  17. int main(array<System::String ^> ^args)  
  18. {  
  19.     test^ t=gcnew test();  
  20.     t->showBase();  
  21.     t->showDerived();  
  22.     return 0;  
  23. }  
The access modifier portrays significant roles in inheritance in order to prevent the access of members inside or outside the assembly.

kewore and description

Abstract class

The abstract classes are used to implement C++ equivalent pure virtual functions. An abstract class is defined by the abstract keyword that prevents you from creating objects of that class type. Unlike interface, we can define the implementation (body) of a function in the abstract class. The polymorphic method implementation must be marked with the override keyword in the derived class as in the following:
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. public ref class absClass abstract  
  5. {  
  6. public:  
  7.     virtual double square(int x) abstract;  
  8.     virtual void show()   
  9.     {  
  10.         Console::WriteLine("showing you in abstract class");  
  11.     }  
  12. };  
  13. public ref class test : absClass  
  14. {  
  15. public:  
  16.     virtual double square(int x) override  
  17.      {  
  18.          return x*x;  
  19.      }  
  20.      virtual void show() override  
  21.      {  
  22.         Console::WriteLine("showing you in derived class");  
  23.      }  
  24. };  
  25. int main(array<System::String ^> ^args)  
  26. {  
  27.     test^ t=gcnew test();  
  28.     Console::WriteLine("square is= {0}",t->square(20));  
  29.     t->show();  
  30.     Console::Read();  
  31.     return 0;  
  32. }  
Exception Handling

The C++/CLI defines try, catch, throw and finally keywords to handle all the run time errors in the code segments. The exception handling implementation is very similar to other CLR supported languages. The following sample handles the array out of bounds error by employing exception handling.
  1. int main(array<System::String ^> ^args)  
  2. {  
  3.     array<int>^ arry= {1,2,3};  
  4.   
  5.     try  
  6.     {  
  7.       for(int i=0;i<=arry->Length;i++)  
  8.       {  
  9.         Console::WriteLine(arry[i]);  
  10.       }  
  11.     }  
  12.     catch(Exception^ ex)  
  13.      {  
  14.          Console::WriteLine(ex);  
  15.      }  
  16.      finally  
  17.      {  
  18.       Console::WriteLine("Exection Done");  
  19.      }  
  20.     Console::Read();  
  21.     return 0;  
  22. }  
The previous sample throws a run time exception that is handled by a try/catch block as in the following:

cli

Delegates

Delegates are special type-safe pointers to methods. They are defined by the delegate keyword in the C++/CLI language as in the following:
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. //delegate definition   
  5. public delegate void testDel(int z);  
  6.   
  7. public ref class test  
  8. {  
  9. public:  
  10.     void square(int x)  
  11.     {  
  12.         Console::WriteLine("Square is=",x*x);  
  13.     }  
  14. };    
  15.   
  16. int main(array<System::String ^> ^args)  
  17. {  
  18.     test^ t=gcnew test();  
  19.     testDel^ td=gcnew testDel(t,&test::square);  
  20.     td(2);  
  21.     Console::Read();  
  22.     return 0;  
  23. }  
Generics Function

Generic functions appear to do the same thing that C++ function templates do. A generic function specification is itself compiled and when you call a function that matches the generic function specification, the actual type is substituted for the type parameters at execution time. No extra code is generated at compile time.

To define delegates, C++/CLI uses the C++ like angle bracket using type parameters that are replaced by the actual type when the function is called as in the following.
  1. #include "stdafx.h"  
  2. using namespace System;  
  3.   
  4. generic<typename T> where T:IComparable  
  5.     T MaxElement(array<T>^ x)  
  6. {  
  7.     T max=x[0];  
  8.     for(int i=1; i< x->Length; i++)  
  9.     {  
  10.         if(max-> CompareTo(x[i]) < 0)  
  11.         {  
  12.          max=x[i];  
  13.         }  
  14.     }  
  15.       
  16. return max;   
  17. }  
  18.   
  19. int main(array<System::String ^> ^args)  
  20. {  
  21.     array<int>^ iData= {3, 20, 4, 12, 7, 9};  
  22.     int maxI= MaxElement(iData);  
  23.     Console::WriteLine("Max Integer is={0}",maxI);  
  24.   
  25.     array<double>^ dData= {4.2, 2.12, 25.7,1.1};  
  26.     double maxD= MaxElement(dData);  
  27.     Console::WriteLine("Max Double is={0}",maxD);  
  28.       
  29.     Console::Read();  
  30.     return 0;  
  31. }  
The previous sample produces a maximum number from an array using a generic function in which the type is not defined. Instead the type, such as integer or double, is defined during execution as in the following.

console output

Resource Management

The C++/CLI code cleans the memory resources by defining the destructor that implicitly called the IDisposable interface.
  1. public ref class test  
  2. {  
  3. public:  
  4.     ~test()  
  5.     {  
  6.         // release resources code  
  7.     }  
  8. };  
The C# using statement releases the resources as soon as it is no longer used. The compile implicitly creates a try/finally statement and invokes the Dispose() method inside the finally block. The C++/CLI also offers this approach but handles this in a more elegant way as in the following:
  1. public ref class test  
  2. {  
  3. public:  
  4.     void hello()  
  5.     {  
  6.         Console::WriteLine("Hello test");  
  7.     }  
  8. };  
  9. int main(array<System::String ^> ^args)  
  10. {  
  11.     //Releasing resources  
  12.     {  
  13.         test t;  
  14.         t.hello();  
  15.     }  
  16.     Console::Read();  
  17.     return 0;  
  18. }  
Native and Managed Code Mixing

The C++/CLI offers a big advantages by allowing the mixture of native C++ code with CLR managed code. This term is referred to as "It just works" in C++/CLI. The following sample illustrates the mixed code by calling the cout method of the native C++ iostream namespace as in the following:
  1. #include "stdafx.h"  
  2. #include <iostream>  
  3. using namespace System;  
  4.   
  5. public ref class test  
  6. {  
  7. public:  
  8.      void managedCode()  
  9.      {  
  10.         Console::WriteLine("Hello test");  
  11.      }  
  12.      //Native code funtion calling  
  13.      void nativeCode()  
  14.      {  
  15.          std::cout << "native code sample";  
  16.      }  
  17. };  
  18. int main(array<System::String ^> ^args)  
  19. {  
  20.     //Releasing resources  
  21.     {  
  22.         test t;  
  23.         t.managedCode();  
  24.         t.nativeCode();  
  25.     }  
  26.     return 0;  
  27. }  
Summary

It is not possible to cover each and every C++/CLI concept in detail in a single article. This article outlines the ohter significant topics such as arrays, control statements, generics, delegates and conditional statements in details by defining their semantics. We also come to understand the C++/CLI OOP concepts such as interface, polymorphism and inheritance with some examples. After finishing this series of articles, one is able to write code in C++/CLI efficiently.


Similar Articles