Managed C++/CLI Programming: Part 2
Abstract
This outlines the rest of the C++/CLI object oriented programming implementations such as inheritance, interface and polymorphism. We'll understand the various control statements such as if, while and do-while, as well as other diverse loops. They include the for loop and switch by applying C++/CLI semantics under a CLR execution model. Apart from that, we'd be confronted with other significant notions such as exception handling, memory management, delegates and generics. Finally, this article illustrates how to mix implementations of native C++ code with managed C++/CLI code under CLR.
Learn Digital Forensics
Control Statements
Control statements define which code should be executed from given statements. C++/CLI proposed if/else, conditional operators and switches as control statements. The if/else construct syntax is very similar to C# coding:
[c]
#include "stdafx.h"
int main(array<System::String ^> ^args)
{
wchar_t ltr;
Console::WriteLine("Enter the Letter");
ltr= Console::Read();
if (ltr >='a')
if(ltr<='z')
{
Console::WriteLine("you have entered small Letter");
}
if (ltr >='A')
if(ltr<='Z')
{
Console::WriteLine("you have entered capital Letter");
}
return 0;
}
The conditional operator in C++/CLI is known as a ternary operator. The first argument must result to Boolean. If the answer is true, the first expression is evaluated. Otherwise, the second answer is:
The switch construct is very similar to C#, but differs in that C++/CLI doesn't support strings with case selection. Instead, we have to use if/else. The following sample gives you a brief of this:
[c]
wchar_t days;
Console::WriteLine("1 = Sunday");
Console::WriteLine("2 = Monday");
Console::WriteLine("Enter your choice");
days= Console::Read();
switch(days)
{
case '1': Console::WriteLine("Sunday");
break;
case '2': Console::WriteLine("Monday");
break;
case '3': Console::WriteLine("Tuesday");
break;
default: Console::WriteLine("Out of Reach");
break;
}
Loop Construct
C++/CLI defines for, for each, while and do-while loops. With loops, code is repeatedly executed until a condition is met. The for, while and do-while loops are syntactically similar to C#:
[c]
//for loop
for(int i=0;i<5;i++)
{
//statements
}
//while loop
int x=0;
while(x<3)
{
//statements
}
//do-while loop
do
{
//statements
}while(i<3);
The for each loop is introduced in C++/CLI. It doesn't exist in ANSI C++ because that uses IEnumerable.
[c]
array<int>^ arry= {1,2,3,4,5};
foreach(int x in arry)
{
Console::WriteLine(x);
}
Arrays
C++/CLI introduced an array keyword in order to implement them. The keyword uses a generic syntax with angle brackets. Angle brackets are used to define the type of element. C++/CLI support array initializers with the same syntax as C#:
[c]
#include "stdafx.h"
int main(array<System::String ^> ^args)
{
//Array Declaration
for each(int i in a1)
{
Console::WriteLine(i);
}
Console::ReadLine();
return 0;
}
Static Members
The static members can be defined by static keyword much like C#. A static field is instantiated only once for all objects of its type. We don't need to instantiate the class in order to access the static members. Instead we can directly access them by using the class type name followed by the "::" operator:
[c]
#include "stdafx.h"
public ref class test
{
public:
static int i;
test()
{
i++;
Console::WriteLine("Constructor Called :{0}",i);
}
int main(array<System::String ^> ^args)
{
test^ obj=gcnew test();
test^ obj1=gcnew test();
//directly access of static member
Console::WriteLine(test::i);
Console::Read();
return 0;
}
Interface
The interface keyword is used to define an interface. Defining interfaces in C++/CLI is similar to C#, but the implementation is slightly different. The method that is defined in the interface must be implemented with virtual keyword in the child class:
[c]
public interface class IDisplay
{
void hello();
public ref class test: IDisplay
{
public:
virtual void hello()
{
Console::WriteLine("Hello test");
}
};
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:
[c]
public ref class baseClass
{
public:
virtual void showBase()
{
Console::WriteLine("base class");
}
};
public ref class test : baseClass
{
public:
void showDerived()
{
Console::WriteLine("derieved class");
}
};
int main(array<System::String ^> ^args)
{
test^ t=gcnew test();
t->showBase();
t->showDerived();
return 0;
}
The access modifier portrayed significant roles in inheritance in order to prevent the access of members inside or outside the assembly.
Abstract class
The abstract classes are used to implement C+ equivalents as a pure virtual function. Abstract classes are defined by the abstract keyword, which prevents you from creating objects of that class type. Unlike interfaces, 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:
[c]
#include "stdafx.h"
public ref class absClass abstract
{
public:
virtual double square(int x) abstract;
virtual void show()
{
Console::WriteLine("showing you in abstract class");
}
};
public ref class test : absClass
{
public:
virtual double square(int x) override
{
return x*x;
}
virtual void show() override
{
Console::WriteLine("showing you in derived class");
}
};
int main(array<System::String ^> ^args)
{
test^ t=gcnew test();
Console::WriteLine("square is= {0}",t->square(20));
t->show();
Console::Read();
return 0;
Exception Handling
C++/CLI defines the try, catch, throw and finally keywords in order to handle all 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 related errors by employing exception handling:
[c]
int main(array<System::String ^> ^args)
{
try
{
for(int i=0;i<=arry->Length;i++)
{
Console::WriteLine(arry[i]);
}
}
catch(Exception^ ex)
{
Console::WriteLine(ex);
}
finally
{
Console::WriteLine("Exection Done");
}
Console::Read();
return 0;
}
The aforementioned sample throws a run time exception which is handled by a try/catch block:
Delegates
Delegates are special types, a safe pointer to methods. They are defined by delegate keywords in the C++/CLI language:
[c]
#include "stdafx.h"
//delegate definition
public delegate void testDel(int z);
public ref class test
{
public:
void square(int x)
{
Console::WriteLine("Square is=",x*x);
}
int main(array<System::String ^> ^args)
{
test^ t=gcnew test();
testDel^ td=gcnew testDel(t,&test::square);
td(2);
Console::Read();
return 0;
}
Generics Function
Generic functions appear to do the same thing as C++ function templates did. Generic function specifications are compiled, and when you call a function that matches the generic function specification, actual type is substituted for the type parameters at execution time. No extra code is generated at compiliation time.
To define delegates, C++/CLI uses the C++ like angle bracket using type parameters that are replaced by actual type when the function is called:
[c]
#include "stdafx.h"
generic<typename T> where T:IComparable
T MaxElement(array<T>^ x)
{
T max=x[0];
for(int i=1; i< x->Length; i++)
{
if(max-> CompareTo(x[i]) < 0)
{
max=x[i];
}
return max;
}
int main(array<System::String ^> ^args)
{
array<int>^ iData= {3, 20, 4, 12, 7, 9};
int maxI= MaxElement(iData);
array<double>^ dData= {4.2, 2.12, 25.7,1.1};
double maxD= MaxElement(dData);
Console::Read();
return 0;
}
The aforementioned sample produced a maximum number from an array using a generic function, in which type isn't defined. Instead it's defined at the calling time, such as integer, doubles like so:
Resource Management
This C++/CLI code cleans the memory resources by defining the destructor which implicitly calls the IDisposable interface.
[c]
public ref class test
{
public:
~test()
{
// release resources code
}
};
C# using statements release resources as soon as it's no longer used. The compile implicitly creates a try or finally statement and invokes the Dispose() method inside the finally block. C++/CLI can also use this approach, but handle it in a more elegant way;
[c]
public ref class test
{
public:
void hello()
{
Console::WriteLine("Hello test");
}
};
int main(array<System::String ^> ^args)
{
//Releasing resources
{
test t;
t.hello();
}
Console::Read();
return 0;
}
Native and Managed Code Mixing
The C++/CLI offers one of the biggest advantages. You can mix native C++ code with CLR managed code. This is referred to as it just works in C++/CLI. The following sample illustrated the mixed code by calling the cout method of the native C++ iostream namespace;
[c]
#include "stdafx.h"
#include <iostream>
public ref class test
{
public:
void managedCode()
{
Console::WriteLine("Hello test");
}
//Native code funtion calling
void nativeCode()
{
std::cout << "native code sample";
}
};
int main(array<System::String ^> ^args)
{
//Releasing resources
{
test t;
t.managedCode();
t.nativeCode();
}
return 0;
}
Summary
Learn Digital Forensics
It's not possible to cover each and every C++/CLI concept in a single article. This article outlines the rest of the significant topics such as arrays, control statements, generics, delegates and conditional statements in detail, by defining their semantics. We also come to understand C++/CLI OOPs concepts, such as interface, polymorphism and inheritance by going through examples. After finishing this series of articles, one can write code in C++/CLI efficiently.