3

I have a method which creates an object in an auto_ptr, sticks that auto_ptr into a vector, and then returns a raw pointer to the object for convenience.

The problem with this design is that it returns a raw pointer. It's really easy for the caller to misunderstand that this pointer is non-owned and wrap the result of this call into a managed pointer, which causes a memory access violation when that memory gets double free'd.

int main (void)
{
  {
    std::auto_ptr<int> foo = ThisMethodReturnsNonOwningPtr(); // Uhoh
  }

  ThisMethodUsesThePtr(); // Crash

  return 0;
}

Ideally, I would like to return something like a pointer which can not be converted to a managed pointer so I don't have to worry about the caller doing this.

Alternatively, I might just return an index into the array, but it's real convenient just being able to pass the pointer around, and who knows maybe I will end up shuffling or sorting the array later.

Raydel Miranda
  • 13,825
  • 3
  • 38
  • 60
QuestionC
  • 10,006
  • 4
  • 26
  • 44
  • 1
    Return a reference instead of a pointer? – dlf Jun 25 '14 at 14:47
  • "something like a pointer which can not be converted to a managed pointer " - you mean something like a reference? – Mike Seymour Jun 25 '14 at 14:47
  • 6
    Sticking `auto_ptr` in a vector is a very bad idea; use `unique_ptr` unless you're stuck in the past. – Mike Seymour Jun 25 '14 at 14:48
  • I would love to return a reference, but we use the NULL return to signify failure and company policy is to basically not use exceptions. Any way to get around that? – QuestionC Jun 25 '14 at 14:50
  • 1
    If you are able to use boost, one option is a `boost::optional<&>`. On the surface this seems like it shouldn't work, but it (mostly) does. See [their documentation](http://www.boost.org/doc/libs/1_55_0/libs/optional/doc/html/boost_optional/optional_references.html). – dlf Jun 25 '14 at 14:51
  • 3
    Related: [N3840 - A Proposal for the World's Dumbest Smart Pointer](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3840.pdf) – ComicSansMS Jun 25 '14 at 15:01
  • Being able to do whatever you want is a strong point of C++. Too much effort spent enforcing policy is at best wasted: A simple comment can be very effective, and anything more often invites circumvention because it is damn-inconvenient and over-broad. – Deduplicator Jun 25 '14 at 15:11
  • @Deduplicator “Being able to circumvent any data-hiding mechanism is a strong point of C++” — [citation needed]. *So very much* needed. – Konrad Rudolph Jun 25 '14 at 15:12
  • @KonradRudolph: Reworded to "Able to do whatever you want". That should be less inflamatory... – Deduplicator Jun 25 '14 at 15:13
  • @MikeSeymour If sticking an auto_ptr into a vector compiles, there's a problem with the compiler or the library. When C++98 was being adopted, one of the national bodies refused to accept it if this compiled. – James Kanze Jun 25 '14 at 15:55
  • @dlf How is `boost::optional` going to do the delete when the time comes? – James Kanze Jun 25 '14 at 15:56
  • @James What I mean is that the owner can keep the object in a `unique_ptr` (or whatever), but return it to callers as a `boost::optional<&>`. So; the optional won't do the delete, because that wouldn't be its job. – dlf Jun 25 '14 at 15:58
  • @dlf So you have a pointer, but you want to wrap it in a class which simulates a pointer? – James Kanze Jun 25 '14 at 16:00
  • @JamesKanze Given the requirements presented for the return value (nullable type, can't be a pointer, must make it clear that the caller can't take ownership), yes. – dlf Jun 25 '14 at 17:04

5 Answers5

2

First, if you can stick an auto_ptr into a vector, it's time to upgrade your compiler or your library. This hasn't been legal since the first standard in 1998. If you've got C++11, you can use std::unique_ptr; if not, you'll need to keep raw pointers in the vector. In both cases, you'll want to wrap the vector: with std::unique_ptr, so that the client doesn't have to do anything fancy to get the actual pointer, and with raw pointers, so that you can manage the memory associated with them.

For the rest, just return a raw pointer. If you have programmers that go around deleting random pointers, you're sunk anyway; most of the time, raw pointers are for navigation, and are just as likely to point to an object with static lifetime as to anything else.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

Stop using auto_ptr and start using unique_ptr or shared_ptr. The latter has reference counting so you can safely return the object and others can use it. The data it points to will not be freed until all references are released.

there is no other alternative if you need to safely return a pointer to the object and still be able to alter the vector. You could use a unique_ptr to prevent a caller from treating the returned pointer as if it owned it.

Last resort is to slap comments on the code to say that the vector owns the object and the pointer is only to be used as a convenience. Its not as good as getting the compiler to check calling code, but it can be the only way if you restrict your options as you mentioned.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
  • 4
    Careful with using `shared_ptr` as a drop-in replacement for such scenarios. Shared ownership is a different ownership model, which potentially changes the destruction order of objects. This can easily lead to major shutdown headaches if used carelessly. – ComicSansMS Jun 25 '14 at 15:07
  • 1
    Never underestimate the power of convention, especially if followed consistently: A comment is often more powerful and flexible than any amount of code. (Using raw pointers only for non-owning use is a good idea.) – Deduplicator Jun 25 '14 at 15:16
  • @ComicSansMS The real issue with `shared_ptr` is the risk of cycles. – James Kanze Jun 25 '14 at 16:12
1

Create a wrapper class template raw_ptr<T> which encapsulates a T*.

If you now implement operator* and operator-> but no conversion operator, you can no longer assign this pointer to another smart pointer, because that assignment would require two custom conversions (raw_ptr<T>T*smart_ptr<T>), which is not allowed in C++.

Notice that, as others have said, you should not use auto_ptr<T> – it’s deprecated for good reasons. Use std::unique_ptr<T> instead. For one thing, you cannot actually safely stick auto_ptrs into a std::vector, great care has to be taken to not accidentally copy (and thus invalidate) such pointers.

Community
  • 1
  • 1
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
0

If you don't want the object to be deleted in conventional means using delete operator, you could make the destructor of the class private and implement destroy() function which self-destructs the object.

Better would be if you could make the function return something like shared_ptr.

A third option could be just to have a class something like template<typename> struct non_ownership_ptr; which the function returns instead of a raw pointer. This pointer class just holds the pointer and you would have to explicitly use auto_ptr<T> p=foo().non_owned_ptr; to avoid (or at least reduce) accidental assignment to auto_ptr or such.

JarkkoL
  • 1,898
  • 11
  • 17
0

I'm always reluctant to write answers that depend on boost since not everyone is able to use it and it's almost always overkill to pull it in for just one little thing, but if you have it anyway, you could return a boost::optional<&>. On the surface it doesn't seem an optional reference could work, but it (mostly) does. See boost's documentation.

This would give you the nullability you require (return boost::none to indicate an error) while making it very clear that the caller is not to take ownership of the object.

dlf
  • 9,045
  • 4
  • 32
  • 58
  • -1 because of the first sentence, it’s a terrible sentiment and bad advice. – Konrad Rudolph Jun 25 '14 at 16:46
  • @Konrad Both are in the first sentence, so what don't you like: the idea of an optional reference or my reluctance to write an answer that depends on boost? – dlf Jun 25 '14 at 17:05
  • 1
    Both. one is a “terrible sentiment” and the other is “bad advice” – in that order. The opposite is true: *do* use Boost, even for small libraries, and *do* promote its use aggressively. – Konrad Rudolph Jun 25 '14 at 17:10
  • @KonradRudolph I love boost, but people disagree on how willing/able they are to bring in additional dependencies (and for what it's worth, I've gotten downvotes both ways). In any case, you're of course under no obligation to explain your voting, so thanks for taking the trouble. – dlf Jun 25 '14 at 17:40