Storing pointer to instance of class with type conversion operator

W

Whitney Kew

Hello,

I have a question regarding type conversion operators. Let's say I
have a simple (nonsensical) class as follows:

class MakeNewInt
{
private:
int* _n;

// By the way, because of the type conversion operator, a
// compiler might not catch an accidental deletion of a
// stack instance - i.e., "delete MakeNewInt(123)" - so,
// add a private operator void*() declaration.
operator void*();

public:
operator int*() const
{
return _n;
}
MakeNewInt(int n) : _n(new int(n)) {}
~MakeNewInt()
{
delete _n;
}
};

Let's also say that I have a method as follows:

void f(int* n)
{
int* q = n;
// Can do other things with q...
}

If I then invoke f() in a main() as such....

int main()
{
f(MakeNewInt(123)); // OK
return 0;
}

....everything is fine; inside f(), I can operate on the pointer q,
knowing that the destructor of MakeNewInt() won't be called until I
exit f().

If, however, I invoke MakeNewInt and attempt to store the result in a
local variable, as such....

int main()
{
int* n = MakeNewInt(123); // n is garbage
return 0;
}

....the destructor of MakeNewInt() has already been called as soon as I
reach the "return 0;" line, and thus the pointer n points to garbage.
Is there anything I can do to allow a caller to use my MakeNewInt class
in this fashion?

Thanks!
Whitney Kew
Software Engineer
 
V

Victor Bazarov

Whitney said:
I have a question regarding type conversion operators. Let's say I
have a simple (nonsensical) class as follows:

class MakeNewInt
{
private:
int* _n;

// By the way, because of the type conversion operator, a
// compiler might not catch an accidental deletion of a
// stack instance - i.e., "delete MakeNewInt(123)" - so,
// add a private operator void*() declaration.
operator void*();

public:
operator int*() const
{
return _n;
}
MakeNewInt(int n) : _n(new int(n)) {}
~MakeNewInt()
{
delete _n;
}
};

Let's also say that I have a method as follows:

void f(int* n)
{
int* q = n;
// Can do other things with q...
}

If I then invoke f() in a main() as such....

int main()
{
f(MakeNewInt(123)); // OK
return 0;
}

...everything is fine; inside f(), I can operate on the pointer q,
knowing that the destructor of MakeNewInt() won't be called until I
exit f().

If, however, I invoke MakeNewInt and attempt to store the result in a
local variable, as such....

int main()
{
int* n = MakeNewInt(123); // n is garbage
return 0;
}

...the destructor of MakeNewInt() has already been called as soon as I
reach the "return 0;" line, and thus the pointer n points to garbage.
Is there anything I can do to allow a caller to use my MakeNewInt class
in this fashion?

int * const & n = MakeNewInt(123);

The object here is bound to a reference to const and shall exist for as
long as the reference exists.

V
 
W

Whitney Kew

Unfortunately, that doesn't work; n points to a pointer that points to
garbage as soon as the statement completes. Plus, I don't want the
caller to have to remember something like that, since I don't
necessarily have control over what the caller writes. I was hoping
that there was some way that I could modify the MakeNewInt class, while
preserving the type conversion operator functionality, so that a caller
could safely make the following call:
int* n = MakeNewInt(123);

Thanks,
Whitney
 
V

Victor Bazarov

Whitney said:
Unfortunately, that doesn't work; n points to a pointer that points to
garbage as soon as the statement completes.

Your compiler might be broken, then. Binding to a const reference should
keep the temporary around well past the declaration statement, AFAIUI.

Try

int * const & n(MakeNewInt(123));

although AFAIK it shouldn't make any difference.
Plus, I don't want the
caller to have to remember something like that, since I don't
necessarily have control over what the caller writes. I was hoping
that there was some way that I could modify the MakeNewInt class, while
preserving the type conversion operator functionality, so that a caller
could safely make the following call:
int* n = MakeNewInt(123);

When is the object going to be destroyed, then? Something has to exist
that defines the lifetime of that temporary. In C++ there are three
different limits for a temporary lifetime. Generally it is the full
expression. Then the initialisation (with which you're struggling). And
then there's binding to a const reference. No matter what you do in your
class, you won't be able to change how the language is specified.

You might want to rethink the need for your users to do such a stupid
thing as holding onto a pointer obtained from a temporary object.

V
 
W

Whitney Kew

Hm, strange. I tried your suggestion of:

int * const & n(MakeNewInt(123));

.... and my compiler couldn't even build it.

OK, let me ask another question: My intended use of this class is for
the callers to use it as in the first case...

f(MakeNewInt(123));

.... Could I somehow prevent the user from using it in the second case;
in other words, at compile time, prevent the user from storing a
temporary object?

Thanks much!
Whitney
 
W

Whitney Kew

Unfortunately, that doesn't work; n points to a pointer that points to
garbage as soon as the statement completes. Plus, I don't want the
caller to have to remember something like that, since I don't
necessarily have control over what the caller writes. I was hoping
that there was some way that I could modify the MakeNewInt class, while
preserving the type conversion operator functionality, so that a caller
could safely make the following call:
int* n = MakeNewInt(123);

Thanks,
Whitney
 
A

adbarnet

Do you need to hold on to the pointer in your creating class? If that is a
requirement, then implement the pointer with reference counting, if it isn't
a requirement, then make a static create method returning a std::auto_ptr
(implicitly passing pointer ownership to the caller).

ad
 
V

Victor Bazarov

Whitney said:
Hm, strange. I tried your suggestion of:

int * const & n(MakeNewInt(123));

... and my compiler couldn't even build it.

OK, let me ask another question: My intended use of this class is for
the callers to use it as in the first case...

f(MakeNewInt(123));

... Could I somehow prevent the user from using it in the second case;
in other words, at compile time, prevent the user from storing a
temporary object?

No. Just like you can't prevent a memory leak in the case

f(new int(123));

(which is probably what you're trying to do). There are some things in
the language that every programmer has to know, and it's not really our
responsibility to provide fool-proof methods for avoiding them. If your
users are so inclined to make such mistakes like holding onto invalid
pointers, they should be programming in Java instead.

V
 
A

adbarnet

There are smart pointer templates you should take a look at - search google,
or look at the Boost or Loki libraries - one form (NoCopy Policy in Loki)
disallows copy and assignment of the pointer variable - you are forced to
use the pointer through an accessor of the containing object. There is no
other way to access the underlying pointer.

You could implement it yourself - but someone's already done it so why
bother?
 
D

David White

Whitney Kew said:
Hm, strange. I tried your suggestion of:

int * const & n(MakeNewInt(123));

... and my compiler couldn't even build it.

OK, let me ask another question: My intended use of this class is for
the callers to use it as in the first case...

f(MakeNewInt(123));

... Could I somehow prevent the user from using it in the second case;
in other words, at compile time, prevent the user from storing a
temporary object?

As far as the class's clients are concerned, what is the difference between
the class you've defined and this one?

class MakeNewInt
{
private:
int _n;
operator void*();
public:
operator int*() const
{
return &_n;
}
MakeNewInt(int n) : _n(n) {}
};

I can't see any difference between them except that your version requires a
lot more work for no benefit (your version needs a copy constructor,
assignment operator and destructor, but mine doesn't). If you agree that the
use of 'new' in your version is redundant, what memory management benefits
could it offer?

DW
 
W

Whitney Kew

Thanks for the replies, everyone.... You're right, Victor, you've
answered my question - I am trying to prevent a memory leak, which is
why I need a destructor, so that I can call some cleanup code, but only
after the function f() completes. Thanks for the sanity check.

Whitney
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top