![]() |
Languages »
C++ / CLI »
General
Beginner
Introduction to Managed C++By Nishant SivakumarAn attempt to get a beginner started on Managed C++ |
C++/CLI, VC7, Windows, .NET 1.0, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
This is intended as a jump-start tutorial for Managed C++ programming. It does not cover every aspect of the managed extensions, but it does cover certain areas that are often puzzling to someone coming from a C# or a native C++ background. The article assumes that the reader is familiar with the basic elements of .NET technology and that the user can understand C++ code when he or she sees it.
#using <mscorlib.dll>
using namespace System;
int _tmain(void)
{
return 0;
}
#using is a preprocessor directive that imports meta data from a .NET
module or library. In our small program we have used #using
on mscorlib.dll which is an essential library for all .NET programs. Just
using #using gives us access to all the namespaces and
classes in that module or library. But we'd have to type the fully qualified
name for each class we use, including the namespace or namespaces if any. We can
use the using namespace directive which will allow us to
use class names without having to use the namespace-name explicitly.
For example I can write this :-
System::Console::WriteLine("Hello Earth");
But if I have used a using namespace directive, then I
can use :-
using namespace System;
...
Console::WriteLine("Hello Earth");
It's mostly a matter of readability. Some people might prefer to use fully qualified names to improve the clarity of their code. And sometimes you are forced to, as when two namespaces have classes with the same names in which case the compiler gets confused.
A managed class is a class that is garbage collected, means you don't have to
delete it after using it. All that's managed for you by the common
language runtime. In MC++ we create managed classes using the __gc
keyword as shown below.
__gc class First
{
public:
First()
{
m_name = "Anonymous";
}
First(String* s)
{
m_name = s;
}
String* GetName()
{
return m_name;
}
void SetName(String* s)
{
m_name = s;
}
private:
String* m_name;
};
Well, it looks more or less like a normal C++ class except that we have used
the __gc keyword when declaring it. That marks it as
managed. You'll also notice how I have used String*
instead of String. This is because the
String class is a managed class that can only be declared on the heap.
First* f1 = new First();
First* f2 = new First("Andrew");
Console::WriteLine(f1->GetName());
Console::WriteLine(f2->GetName());
f2->SetName("Peace");
Console::WriteLine(f2->GetName());
Using the managed class is not dissimilar from using a normal old-style C++
class. Except that I cannot declare objects of type First,
I have to declare them on the heap using First* and
new. As you might observe, I have not called delete.
Hey, don't look at me with that sort of gleam in your eyes as if to tell me that
I should not be so careless. I didn't call delete because
it's not required that I do so. This is Managed C++ we are talking about,
remember. And because First is a managed class, the
common language runtime will automatically delete the
objects that are no longer under use, using it's garbage collector. Very handy,
I say very handy!
Take a look at the two listings below :-
//Listing A
f2->SetName("Peace");
Console::WriteLine(f2->GetName());
//Listing B
f2->Name = "Colin";
Console::WriteLine(f2->Name);
Obviously the second listing is more readable. Of course using a public member variable would be a very impetuous idea and one that would result in a lot of scornful looks from most programmers. But in .NET we have properties, in fact I have some rather vague ideas that the native C++ compiler supports properties too, but since the vagueness is rather strong, I better keep quiet about that one.
__gc class First
{
public:
...
__property String* get_Name()
{
return m_name;
}
__property void set_Name(String* s)
{
m_name = s;
}
...
};
If you have only a get_ function then your property is
read-only. If you have only the set_ function you have a
write-only property. And in our case, since we have both get_
and set_ functions, we have a read-and-write property.
The get_ function's return type must be same as the
set_ function's argument. MSDN adds that you cannot define a property
that has the same name as the containing class.
Consider the function below :-
void Show(Object* o)
{
Console::WriteLine(o);
}
Rather neat, eh? It takes an object of type Object
and displays it on the console. Now see this snippet of code.
String* s1 = "Hello World";
Show(s1);
Compiles and works fine. Now see the following snippet of code.
int i = 100;
Show(i); //This won't compile
Blast! That won't even compile. You'll get compiler error C2664: 'Show'
: cannot convert parameter 1 from 'int' to 'System::Object
__gc *'. This is where we need to use the __box
keyword.
int i = 100;
Show(__box(i));
What __box does is simple. It creates a new managed
object on the heap and copies the value type object into the managed object. And
it returns the managed object which is actually a copy of the original value
type object. This means that if you modify the managed object returned by an
__box, the original object won't change at all.
Obviously if you can box, you should be able to unbox too, eh? Let's say we
want to box a value object to an Object and then unbox
it back to a value object. The following code snippet shows you how to
accomplish this chore.
Object* o1 = __box(i);
int j = *static_cast<__box int*>(o1);
j *= 3;
Show(__box(j));
Good heavens! That turned out to be rather more sinister looking than you had
expected I bet. What we do is to cast the managed object to the
__gc pointer on the CLR heap and then we simply
dereference it. You can use dynamic_cast instead of
static_cast for increased type-safety.
Consider the following function.
void NativeCall()
{
puts("This is printed from a native function");
}
As you can see, it uses purely normal non-.NET stuff. Obviously we can
improve execution speed if we can compile this function as native. MC++ provides
us with the #pragma managed and #pragma
unmanaged preprocessor directives. We simply add a #pragma
unmanaged just before the function and then add a #pragma
managed just after the function.
#pragma unmanaged
void NativeCall()
{
...
}
#pragma managed
Now when this function is encountered during program execution, the common
language runtime will pass on control to the native platform. Obviously we must
use this whenever we have functions that use fully unmanaged functions. What's
real neat is that we call call unmanaged functions that are marked as
#pragma unmanaged from within managed blocks of code.
Earlier I had said that managed classes cannot be allocated on the stack.
Sometimes, we might have the requirement for a very simple class, often merely
for storing some values. In such cases it might be desirable to have a value
type class that can be allocated on the stack. That's where the
__value keyword comes in. The following code snippet
shows you how to declare a value type class.
__value class Second
{
public:
void Abc()
{
Console::WriteLine("Second::Abc");
}
};
Now we can declare objects of type Second
on the stack.
Second sec;
sec.Abc();
In fact we can even declare them on the heap. But remember this won't be the
CLR managed heap and thus we need to delete our objects on our own.
Second* psec = __nogc new Second();
psec->Abc();
delete psec;
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 7 Jul 2002 Editor: Nishant Sivakumar |
Copyright 2001 by Nishant Sivakumar Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |