5
votes

I want to use smart pointer in the following situation:

   SOME_STRUCT* ptr = new SOME_STRUCT;
   ptr->SOME_MEMBER = new BYTE[100];
   CallSomeAPI(ptr);

now the API can either return error or pass successfully but in both those cases i want to delete the object , one way can be to write delete statement during error exit and during normal exit.

But how can i use a smart pointer for these pointers ? By smart pointers i mean unique_ptr, shared_ptr , etc. whichever can work !

Thanks!

3
Why do you want to use a std::unique_ptr? The new smart pointers shouldn't really be seen as a replacement for normal pointer, but more like handling ownership: Can a resource (memory, file, device, etc.) be owned by multiple owners (std::shared_ptr) or by only a single owner (std::unique_ptr) at a time?Some programmer dude
Actually i want to use a smart pointer to automatically deallocate , not necessarily unique_ptr , my main objective is automatic cleanup and not ownership. I edited the question now to be more clear.stng
The fact that a function takes a pointer does not imply that it must be allocated dynamically. Since you also say that the objects don't leave the function with that code, create both objects on the stack to get automatic cleanup.Ulrich Eckhardt
SOME_STRUCT s; std::vector<BYTE> buffer(100); s.SOME_MEMBER = &buffer[0]; CallSomeAPI(&s); -- yes, this works.Ulrich Eckhardt
@Ulrich Don't be shy! Post an answer!TobiMcNamobi

3 Answers

3
votes

You can write a custom deleter for unique_ptr.

struct my_deleter {
    void operator()(SOME_STURCT* ptr) const {
        delete[] ptr->SOME_MEMBER;
        delete ptr;
    }
};

using my_ptr = std::unique_ptr<SOME_STRUCT, my_deleter>;

and I would suggest changing new SOME_STRUCT; to new SOME_STRUCT{}; to default initialize SOME_MEMBER to nullptr.

I am not 100% happy with this solution, so perhaps look into scope_guard or writing a wrapper class for your struct.

2
votes

I assume you cannot modify SMOE_STRUCT to add a destructor to it. This leaves you with two options: a custom deleter, and encapsulation.

First, you could create a custom deleter for use with std::unique_ptr:

struct SOME_STRUCT_Deleter
{
  void operator() (SOME_STRUCT *p) const
  {
    delete[] p->SOME_MEMBER;
    delete p;
  }
};

std::unique_ptr<SOME_STRUCT, SOME_STRUCT_Deleter> ptr{new SOME_STRUCT};
ptr->SOME_MEMBER = new BYTE[100];
CallSomeAPI(ptr.get());

If you find that, unlike the situation described in your question, shared ownership would suit you better than exclusive one, you can use the deleter with a shared_ptr as well, like this:

std::shared_ptr<SOME_STRUCT> ptr{new SOME_STRUCT, SOME_STRUCT_Deleter{}};
ptr->SOME_MEMBER = new BYTE[100];
CallSomeAPI(ptr.get());

A second option, which I find preferable, is to wrap SOME_STRUCT:

struct SOME_STRUCT_plus_plus
{
  SOME_STRUCT s;
  ~SOME_STRUCT_plus_plus()
  {
    delete[] s.SOME_MEMBER;
  }

  SOME_STRUCT_plus_plus()
  {
    s.SOME_MEMBER = new BYTE[100];
  }
};

std::unique_ptr<SOME_STRUCT_plus_plus> ptr{new SOME_STRUCT_plus_plus};
CallSomeAPI(&ptr->s);

You could even "wrap" it by making SOME_STRUCT_plus_plus derive from SOME_STRUCT instead of aggregating it, which would give you direct access to members without the need to go through s. At the same time, it could lead to memory leaks if someone cast SOME_STRUCT_plus_plus* to SOME_STRUCT* and then called delete on it.

1
votes

Here, it seems that all can be on the stack:

SOME_STRUCT ptr;         // or auto ptr = std::make_unique<SOME_STRUCT>();
BYTE bytes[100];         // or std::vector<BYTE> bytes(100);
ptr.SOME_MEMBER = bytes; // or ptr->SOME_MEMBER = bytes.data();
CallSomeAPI(&ptr);       // or CallSomeAPI(ptr.get());