5

Couldn't figure out how to word the question accurately, so here's an example:

Given this function prototype:

void Foo(myClass* bar);

I want to prevent this usage:

Foo(new myClass());

and instead require a previously created object:

myClass* bar = NULL;
bar = new myClass();
Foo(bar);

or

myClass bar;
Foo(&bar);

Thanks.


EDIT

Here's a clarified example:


void Mouse::SetImage(BITMAP* image, int focusX, int focusY) {
    if(_image) {
        set_mouse_sprite(NULL);
        set_mouse_sprite_focus(0, 0);
        show_mouse(NULL);
        destroy_bitmap(_image);
        _image = NULL;
    }
    if(image) {
        _image = create_bitmap(image->w, image->h);
        clear_bitmap(_image);
        blit(image, _image, 0, 0, 0, 0, image->w, image->h);
    }
    if(image == NULL) {
        focusX = 0;
        focusY = 0;
    }
    _focusX = focusX;
    _focusY = focusY;
    _dirtyImage = true;
}

Whatever image the user passes in gets copied to the object's image.

If I deallocate the passed in image after copying it and the image is used elsewhere in the program it will crash the program with an access violation.

If they allocate the storage in-line and I don't deallocated it, a memory leak occurs. The problem is compounded if they call the SetImage method multiple times over the course of the running program.

Comments about using alternative libraries or on the Allegro Library itself will be ignored, I already know it's horrible. I don't have a choice.

Casey
  • 8,686
  • 9
  • 52
  • 79
  • 9
    Why would you want to do that? As far as I know, there's no general way to know the difference. – Dean Harding Aug 15 '10 at 20:42
  • I don't know why you want this, but I could make an assumption that you don't want callers to think your code will deallocate for them. If you want to guarantee that even if they instantiate it inline, the destructor would get called, you could take a reference to a smart pointer class, instead of a raw pointer. – Merlyn Morgan-Graham Aug 15 '10 at 21:14
  • 1
    So what do you need? Besides a miracle. Macros. Lots of macros. – Joey Adams Aug 15 '10 at 21:19
  • I didntget the question . Can some one edit and explain the question please – mousey Aug 15 '10 at 21:32

5 Answers5

19

Your design needs to make a choice. Either take ownership and delete it, or don't take ownership. Either way, it's up to the user to know how to use your function. They either need to know that your function will destroy the image (and maybe pass their own copy as needed), or they need to be smart enough to manage their own resources.

Typically, you don't want to steal ownership away just to delete it. So I would not delete anything. If someone is silly enough to lose the ability to delete the image they pass, that's not this functions problem. In other words, you should try to protect against Murphy, but forget about protecting against Machiavelli.

That said, raw pointer use is bad! Poor C++ code is marked by manual resource management and resource issues. You should have a wrapper around an image, that will delete the image in the destructor. That way you can never leak, even if an exception is thrown. Provide it with a reset() method which discards it's old image resource and gets a new one.

It sounds like you want shared ownership, so you'll want a reference counted resource wrapper. The issue is then solved: if someone does an "inline" allocation, it'll be put into the shared pointer and then deleted automatically when it's done. (And even better is to have an explicit constructor so someone has to know they'll be sharing the resource.)

This is done in a smart pointer called shared_ptr. Boost has one, TR1 has one, and C++0x has one. Just give it a custom deleted (one that frees the image), and you never worry about resource management again.

This should be done with all resources. The concept here is Scoped-bound Resource Management (SBRM); that a resource is managed automatically by taking advantage of the lifetime rules of automatic (stack) variables. It's known alos as it's original but uglier name Resource-Acquisition Is Initialization (RAII). Do some research into this area and you'll find your code is easier and cleaner.


It cannot be done without changing the type of the parameter. You could change it to:

void Foo(myClass*& bar);

Because a non-const reference can only be bound to an lvalue:

void foo(int*&);

int main(void)
{
    int *i = 0;
    int j;

    foo(i); // well-formed
    foo(&j); // ill-formed
    foo(new int); // ill-formed
}

However, this disallows taking the address of an lvalue. You can of course do the simple:

int main(void)
{
    int j;
    int* pj = &j;
    foo(pj); // well-formed
}

And it works. But I don't know why you'd want to do any of this.


The above solution would allow you to modify the argument (because it's a reference). If you wanted to enforce const within the function, you could make a utility like this:

template <typename T>
class require_lvalue
{
public:
    require_lvalue(T& pX) :
    mX(pX)
    {}

    const T& get(void) const
    {
        return mX;
    }

    operator const T&(void) const
    {
        return get();
    }

private:
    // non-copy-assignable
    require_lvalue& operator=(const require_lvalue&);

    const T& mX;
};

void foo(require_lvalue<int*>);

Same result, except you have a const-reference within the function.


Note that MSVC has a bug, and accepts this:

foo(new int);

in both cases, even though it shouldn't. (It does not accept new int(), however.)

Community
  • 1
  • 1
GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • You were quicker, +1 ;-) but I prefer wrappers for such cases anyway. – Roman Nikitchenko Aug 15 '10 at 21:19
  • 1
    However the caveat: Function 'foo' now has the potential to change the input pointer 'bar' as it is being passed by reference. Changing the input parameter to be a non-const reference gives indication to maintainers of the code, that it is OK to modify the input parameter. So the interface of the fuction now become misleading... – Chubsdad Aug 16 '10 at 02:32
  • 1
    @chubsdad: Did you even read the answer? The second half addresses that. – GManNickG Aug 16 '10 at 03:01
  • @chub: No problem, I just re-read my comment and saw how mean that sounded, I intended to throw a smile in there like: "Did you even read the answer? **:)** ..." Sorry. – GManNickG Aug 16 '10 at 03:53
  • @Gman: I understand now, that I must inform my users that they should RTFM :) (Good thing that I am developing an API Reference manual concurrently to writing this engine!) +1. – Casey Aug 16 '10 at 04:27
  • @Casey: Indeed, ultimately it's up to the user to know what your function does. But do consider using a `shared_ptr` of some type, or at least making your own. You should never be in a position to have to manually delete things. – GManNickG Aug 16 '10 at 04:40
  • @GMan: Unfortunately, I can't use a shared_ptr. The engine is wrapped around the Allegro library and the Allegro library has no idea what a shared_ptr is. – Casey Aug 16 '10 at 16:36
  • @Casey: Well you have to make one or use one, and you can use `.get()` to get the raw pointer. – GManNickG Aug 16 '10 at 18:42
  • @GManNickG: I don't think MSVC allowing non-const references to temporaries is a bug, but an intended extension to the language. – porges Sep 25 '12 at 04:50
4

It's impossible to have such a distinction of usage. And in all the cases it's a valid parameter. I really can't understand why you need this...

pagra
  • 665
  • 4
  • 11
2

So don't use pointers... use (lvalue) references:

void Foo(myClass& bar);
Sadeq
  • 7,086
  • 4
  • 29
  • 44
2

Doesn't this solve your task? But I anyway recommend something like std::auto_ptr for such cases.

#include <iostream>

void test(int *& ptr)
{
    std::cout << *ptr << std::endl;
}

int main()
{
/* NEXT LINE WILL FAIL */
//  test(new int(5));

    int *b = new int(5);
    test(b);
    delete b;

    return 0;
}
Roman Nikitchenko
  • 11,946
  • 7
  • 65
  • 101
-1

C or C++ does not give you the luxury of determining where the memory was allocated, that come into your function's parameters. If you want better safety then program in .NET.

If you want to make it safer, than just completely change your function signature to accept an auto pointer. that way the semantics become crystal clear, and there should be no confusion as to whom or what owns the memory.

C Johnson
  • 14,441
  • 9
  • 56
  • 70