2

I'm trying to build a struct with a constructor and destructor in C++ (is that a bad thing to do? Should I use a class instead?), because I would hate to write unnecessary 10 lines of delete struct.member every time the members should be deallocated

The members of the struct are mostly pointers to other types.

However, apart from the option of the member pointer being a unique pointer, I also want to be able to assign a copy of another pointer to a member pointer in the struct. Consequently, if I try to deallocate that memory inside the destructor of the struct, it could have already been deallocated, causing a crash. (especially if the pointer copied was from another object of the same struct)

ptr to ptr-types instead of ptr-types in the struct wouldn't solve the problem either (I think), since I also want to allow the member to be able to be a unique pointer to an object.

A possible solution that I've thought of, is to have both a ptr-to-ptr and a ptr to be pointed to by the ptr-to-ptr. But that would just be rather inefficient.

What would my best solution be?

I hope (but doubt) my question is clear enough.

Here's some example code that might help.

struct GraphicsDesc
{
public:
ID3D11VertexShader*         pSolidColorVS;
ID3D11PixelShader*          pSolidColorPS;
ID3D11InputLayout*          pInputLayout;
ID3D11ShaderResourceView*   pColorMap;
ID3D11SamplerState*         pSampler;
ID3D11BlendState*           pBlendState;
ID3D11Buffer*               pVertexBuffer;
UINT                        VertexSize;
D3D_PRIMITIVE_TOPOLOGY      Topology;

GraphicsDesc() 
{
    pSolidColorVS   = nullptr;
    pSolidColorPS   = nullptr;
    pInputLayout    = nullptr;
    pColorMap       = nullptr;
    pSampler        = nullptr;
    pBlendState     = nullptr;
    pVertexBuffer   = nullptr;
    VertexSize      = 0;
    Topology        = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
}

virtual ~GraphicsDesc() 
{
    if (pVertexBuffer) {
        pVertexBuffer->Release();
    }
    if (pBlendState) {
        pBlendState->Release();
    }
    if (pSampler) {
        pSampler->Release();
    }
    if (pColorMap) {
        pColorMap->Release();
    }
    if (pInputLayout) {
        pInputLayout->Release();
    }
    if (pSolidColorPS) {
        pSolidColorPS->Release();
    }
    if (pSolidColorVS) {
        pSolidColorVS->Release();
    }
}
};
Alok Save
  • 202,538
  • 53
  • 430
  • 533
xcrypt
  • 3,276
  • 5
  • 39
  • 63

1 Answers1

3

All of those pointer data members are pointers to COM types that are reference counted. Release does not necessarily destroy the pointed-to object. It decrements the reference count and only destroys the object if there are no more references to it.

Rather than using raw pointers and calling AddRef and Release yourself, you should use CComPtr and CComQIPtr, which automate the reference counting.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 1
    The highest voted answer in http://stackoverflow.com/questions/2831438/using-mem-fun-ref-with-boostshared-ptr shows how to do it using shared_ptr in case you want to go down that path. – Laserallan Sep 10 '11 at 17:38
  • @Laserallan You mean [this one](http://stackoverflow.com/questions/2831438/using-mem-fun-ref-with-boostshared-ptr/2831765#2831765)? :) – Mateen Ulhaq Sep 10 '11 at 17:50
  • Right, if I understand it correctly the shared_ptr will ultimately call delete (p) instead of p->Release()... I could make my own implementation of a shared pointer that calls release instead of delete? Is that hard? Is that what the CComPtr does? – xcrypt Sep 10 '11 at 17:54
  • Found how to link to answers now :) I actually think the accepted answer is nicer in many ways, does not require boost too. http://stackoverflow.com/questions/2831438/using-mem-fun-ref-with-boostshared-ptr/2831452#2831452 – Laserallan Sep 10 '11 at 17:56
  • xcrypt: Yes and no, that's the default behavior. You can override the delete behavior by giving a function to the shared_ptr constructor as its second parameter. The important thing is to remember to get it right every time. I suppose that is a case for CComPtr which gets it right for com objects automatically. – Laserallan Sep 10 '11 at 17:59
  • Alright... I'll try to make my own implementation of a shared_ptr that calls release instead of delete so I understand what's going on :) – xcrypt Sep 10 '11 at 18:02
  • @xcrypt: Wait, what? No... use `CComPtr`; that's what it's for. If you want to know what it does and how it works, look at its source (in `` or something included from there). – James McNellis Sep 10 '11 at 18:05
  • @Laserallan: The advantage of `CComPtr` is that it utilizes the built-in internal `IUnknown` reference counting; the use of `shared_ptr` with a custom deleter imposes the unnecessary overhead of external reference counting. – James McNellis Sep 10 '11 at 18:05
  • what's the difference between CComPtr and CComQIPtr then? – xcrypt Sep 10 '11 at 18:09
  • `CComQIPtr` encapsulates a call to `QueryInterface`. You can find out all about both of these classes (and the rest of ATL) on MSDN. – James McNellis Sep 10 '11 at 18:11
  • @James McNellis One last question: is it safe to assign a raw pointer to a CComPtr as long as I release the raw pointer properly? Because I really hate to use those pointer helper classes unless they are absolutely necessary... – xcrypt Sep 10 '11 at 21:51
  • You should use those smart pointer classes all of the time. Writing correct code without using smart pointers is far more difficult than most people realize. – James McNellis Sep 11 '11 at 03:43
  • Which is exactly why I don't want to use smart pointers unless they are absolutely necessary... It's fun working with raw pointers :) I don't want to miss out on the fun parts! - but to be more specific: I wasn't asking 'should/shouldn't I?' I was asking 'can I?' – xcrypt Sep 11 '11 at 12:26