thomasb
thomasb

Reputation: 6045

Do I need to delete unmanaged objects in C++/CLI

I have a C++/CLI method calling C structures and methods:

In the C header:

typedef struct {
  int left;
  int top;
  int right;
  int bottom;
} frame_t;

typedef struct {
  int     width;
  int     height;
  int     group;
  int     nb_hints;
  frame_t *hints;

  frame_t view;
  frame_t dest;
  int     page;
} image_t;

int resize_to_fill(image_t *image, int width, int height);

The C++/CLI wrappers:

public ref class Frame {
public:
    int Left;
    int Top;
    int Right;
    int Bottom;

    property int Width {
        int get() {
            return (Right - Left);
        }
    }
    property int Height {
        int get() {
            return (Bottom - Top);
        }
    }

    Frame() {
        Top = 0;
        Left = 0;
        Bottom = 0;
        Right = 0;
    }

internal:
    frame_t ToNative() {
        frame_t f;
        f.left = Left;
        f.top = Top;
        f.right = Right;
        f.bottom = Bottom;
        return f;
    }

    Frame(const frame_t &f) {
        Left = f.left;
        Top = f.top;
        Right = f.right;
        Bottom = f.bottom;
    }
};

public ref class Image {
private:
    void init(int nb_hints) {
        this->view = gcnew Frame();
        this->dest = gcnew Frame();
        this->page = 0;
        this->Hints = gcnew array<Frame^>(nb_hints);
    }

public:
    int Width;
    int Height;
    array<Frame^>^ Hints;

    property int Group {
        int get() {
            return group;
        }
    }
    property int Page {
        int get() {
            return this->page;
        }
    }
    property Frame^ View {
        Frame^ get() {
            return this->view;
        }
    }
    property Frame^ Dest {
        Frame^ get() {
            return this->dest;
        }
    }

    Image(int nb_hints) {
        this->init(nb_hints);
    }

    Image() {
        this->init(0);
    }

internal:
    Frame^ view;
    Frame^ dest;
    int page;
    int group;

    Image(image_t native) {
        this->Width = native.width;
        this->Height = native.height;

        this->Hints = gcnew array<Frame^>(native.nb_hints);
        for (int i = 0; i < native.nb_hints; i++)
        {
            this->Hints[i] = gcnew Frame(native.hints[i]);
        }

        this->group = native.group;
        this->page = native.page;
        this->dest = gcnew Frame(native.dest);
        this->view = gcnew Frame(native.view);
    }

    image_t ToNative() {
        image_t i;
        i.width = this->Width;
        i.height = this->Height;
        i.group = this->Group;

        // hints
        i.nb_hints = this->Hints->Length;
        i.hints = new frame_t[this->Hints->Length];
        for (int nb = 0; nb < this->Hints->Length; nb++)
        {
            i.hints[nb] = this->Hints[nb]->ToNative();
        }

        // output values
        i.page = this->Page;
        i.view = this->View->ToNative();
        i.dest = this->Dest->ToNative();

        return i;
    }
};

// later, in another class
static int ResizeToFill(Image^ %image, int width, int height) {
    image_t native = image->ToNative();

    int result = resize_to_fill(&native, width, height);

    image = gcnew Image(native);

    // do I need to do this ?
    delete *native;

    return result;
}

Do I need to delete the native structure instance ?

I've searched for this answer already, but it's much more often about disposing of managed resources than unmanaged ones, which seems to be evident to most people. I'm new to C/C++ so I don't have all the memory management reflexes.

Upvotes: 1

Views: 1214

Answers (2)

Hans Passant
Hans Passant

Reputation: 942538

image = gcnew Image(native);

It entirely depends on what this statement does. The library you are using is not a common one and that Image wrapper class is not System::Drawing::Image so there's no way to tell from just your snippet.

There are two ways the programmer of this library could have implemented it. If he did the smart way then he made it your problem and created a shallow copy. In other words, a new instance of Image that shares the same underlying pixel buffer as native. Or he could have created a deep copy and created a new pixel buffer for the Image object. In which case you can safely destroy the image_t object.

That difference exists in .NET as well, Bitmap::Clone() makes a shallow copy, the Bitmap(Image^) constructor makes a deep copy. There is a huge difference between the two, the shallow copy takes a few handfuls of nanoseconds at most. The deep copy takes much, much longer if the image is large, could be milliseconds.

Dealing with a shallow copy if quite painful, you probably can't change the finalizer of the Image class. You have to keep the image_t* around and get a good signal that the Image object is no longer in use so you can safely delete it. You'll have to significantly restructure your code. If you guessed wrong then you got a pretty nasty problem, it isn't guaranteed to crash your code. You've got a dangling pointer that isn't that likely to cause an access violation. Seems to work just fine when you test, corrupts the displayed image or crashes your program undiagnosably when you're not looking.

Very important to look at the library's source code or documentation to know what it does.

Upvotes: 1

Akbar Oripov
Akbar Oripov

Reputation: 19

You only need to deallocate memory for the objects that are created dynamically, static objects will be destroyed by destructors when variable goes out of scope.

Upvotes: 1

Related Questions