Jeli to ime cure ili jos gore, mame ze klasu?! LOL
Evo ti jedan dio iz knjige "Programming with Managed Extensions for Microsoft Visual C++ .NET":
Citat:
C++ Standard Library
You can use the C++ standard library, but be aware that you cannot use it with managed types. So you can neither put managed objects directly into Standard Template Library (STL) containers, nor write stream operators for managed types. If you want to integrate managed objects and native objects, you have to write wrapper classes, either managed wrappers around your native types so that they can be put into a managed container or a native wrapper so that a managed type can be put into an STL container. STL code can generate C++ exceptions, so if your code uses STL, you have to enable exception handling with the /EH switch.
Managed Pointers in Unmanaged Types
I mentioned earlier that an unmanaged type cannot contain a managed pointer because the garbage collector will not be able to track the managed pointer. However, there are cases when it is useful for an unmanaged type to treat a managed type as a “data member,” and to do this, the unmanaged type must explicitly handle the lifetime of the managed object.
The .NET Framework has a class named GCHandle that represents a reference on a managed object. A GCHandle is a value type that has sequential layout and does not have managed members, but because of the bug I mentioned earlier (the compiler ignores the sequential layout attribute), GCHandle cannot be a member of a __nogc type. A GCHandle can be converted to an IntPtr—in other words, as a platform specified integer—which can be converted to an int, and hence, a GCHandle can be stored indirectly in a __nogc type.
GCHandle does not have an accessible constructor; instead, you initialize an instance by calling the Alloc method. This method is overloaded, and both versions take an object reference from which the GCHandle will be initialized. The value in the GCHandle instance acts like a pinning pointer: while the value exists, the object will be pinned, so it will not be moved in memory or collected. However, unlike a pinning pointer, you must take explicit steps to unpin the object by calling GCHandle::Free, as shown in this example:
// filewriter.cpp
__nogc class FileWriter
{
public:
FileWriter(String* name)
{
StreamWriter* sw = File::AppendText(name);
GCHandle h = GCHandle::Alloc(sw);
IntPtr ptr = GCHandle::op_Explicit(h);
writer = ptr.ToInt32();
}
~FileWriter()
{
StreamWriter* sw = GetStream();
sw->Close();
GCHandle h = GCHandle::op_Explicit(IntPtr(writer));
h.Free();
}
void Write(String* s)
{
StreamWriter* sw = GetStream();
if (sw) sw->Write(s);
}
private:
int writer;
StreamWriter* GetStream()
{
GCHandle h = GCHandle::op_Explicit(IntPtr(writer));
return static_cast<StreamWriter*>(h.Target);
}
// Prevent copying, see later
FileWriter(const FileWriter&){}
operator=(const FileWriter&){}
};
This unmanaged class wraps a StreamWriter object; the object is created in the constructor, and it is released in the destructor. Because this class is unmanaged, it can be created on the stack. Therefore, it allows you to hold onto the managed resource only as long as the stack frame survives.
The constructor takes a managed string as a parameter. This arrangement is fine because the managed string reference lives only as long as the stack frame of the method call. The constructor uses this reference to create a StreamWriter object. The constructor then creates a GCHandle based on this object—which effectively pins the object. The handle is first converted to an IntPtr and then converted to an int so that the handle can be stored in the unmanaged type.
The instance of the unmanaged FileWriter class must release its managed resources when it is destroyed. The first action the object performs is to close the file. The object then unpins the StreamWriter object by converting the integer to an IntPtr, converting the IntPtr to a GCHandle, and then calling Free. The private method GetStream returns the StreamWriter object by accessing the GCHandle::Target property, which is the object that has been pinned.
You might decide to hold more than one managed type in an unmanaged class. However, the code to access these objects and their GCHandle structures would get rather messy. To help out, the C++ team has provided the unmanaged template gcroot<> in gcroot.h, as shown here:
template <class T> struct gcroot
{
gcroot();
gcroot(T t);
gcroot(const gcroot& r);
~gcroot();
gcroot& operator=(T t);
gcroot& operator=(const gcroot &r);
operator T () const;
T operator->() const;
};
The template parameter, T, is a managed pointer type. The class will always create a GCHandle for the managed object passed to the constructor; the copy constructor and assignment operator also create new handles based on the object that the gcroot<> instance wraps. This arrangement is so that if an instance of a class that uses a gcroot<> is copied, there will be two separate GCHandle structures for the same object. This duplication is needed because if both instances of gcroot<> had the same handle, destroying one of the instances would invalidate the other instance of gcroot<>. (In the preceding FileWriter example, I have ignored this issue by making the copy constructor and assignment operator private.)
The gcroot<> template has conversion operators that return the object wrapped by the handle and an operator-> so that you can treat the gcroot<> as a smart pointer class. The FileWriter class using gcroot<> looks like this:
// filewriter2.cpp
__nogc class FileWriter
{
gcroot<StreamWriter*> writer;
FileWriter(const FileWriter&){}
operator=(const FileWriter&){}
public:
FileWriter(String* name)
{
writer = File::AppendText(name);
}
~FileWriter()
{
writer->Close();
}
void Write(String* s)
{
writer->Write(s);
}
};
I think you’ll agree that this code is far clearer than the previous version.