31
#include <memory>

class bar{};

void foo(bar &object){
    std::unique_ptr<bar> pointer = &object;
}

I want to assign an address of the object to the pointer. The above code obviously wont compile, because the right side of the assignment operator needs to be a std::unique_ptr. I've already tried this:

pointer = std::make_unique<bar>(object)

But it throws many errors during compilation. How can I do that?

Update
As said in the answers - using the std::unique_ptr::reset method led to undefined behaviour. Now I know, that in such cases I should use a standard pointer.

Tomasz Kasperczyk
  • 1,615
  • 2
  • 17
  • 38
  • What is `olt` and what are the errors ? – SirDarius Feb 13 '15 at 14:50
  • 6
    Why would you do that? Smart pointers are for managing the lifetime of objects and you do not own `object` in the first place. – Baum mit Augen Feb 13 '15 at 14:50
  • @SirDarius My bad, I've already corrected the error. – Tomasz Kasperczyk Feb 13 '15 at 14:51
  • @BaummitAugen Well, I thought that I should use smart pointers as a replacement for the standard pointers. Am I wrong? – Tomasz Kasperczyk Feb 13 '15 at 14:52
  • 1
    You are. Only use smart pointers as a replacement for "owning" raw pointers – Chris Drew Feb 13 '15 at 14:52
  • 7
    Using the method described in the answers will lead to [*undefined behavior*](http://en.wikipedia.org/wiki/Undefined_behavior) when the smart pointer tries to delete the pointer. Listen to Baum, the new smart pointers are more for *ownership* than for replacement of raw pointers. – Some programmer dude Feb 13 '15 at 14:53
  • 4
    @user3125731 Yes you are wrong. Use smart pointers to manage the lifetime of object, i.e. if you own the object through said pointer. If you merely want to use an existing object whose lifetime is managed elsewhere, a normal pointer or reference is the way to go. – Baum mit Augen Feb 13 '15 at 14:58

6 Answers6

24

Try std::unique_ptr::reset

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

But be aware this is not recommended, you should not create a unique_ptr to a reference that is being passed to a function. At the end of the function, when pointer is being destroyed it will try to destroy object as well, and it won't be available outside the function call, resulting in an access memory error.

Example: This will compile, but give a runtime error.

struct bar{ int num;};

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

int main()
{
    bar obj;
    foo(obj);
    obj.num; // obj is not a valid reference any more.
    return 0;
}

On the other hand you might want to consider using shared_ptr this can help you to decide: unique_ptr or shared_ptr?.

Alexis Wilke
  • 15,168
  • 8
  • 60
  • 116
Raydel Miranda
  • 12,567
  • 2
  • 30
  • 56
  • 3
    Please I would be very glad if someone explain to me why the down vote. – Raydel Miranda Feb 13 '15 at 18:02
  • The most common case is when a library function returns a raw ptr and you want to "scope" it. Exemple : std::unique_ptr p=someObject->create(...); where someObject->create returns a SomeObject* and you have no control over it. – Dinaiz Nov 24 '16 at 12:00
  • 1
    @RaydelMiranda Your example is not good. The `bar obj` object is not allocated, so it's not really the example you wanted to talk about. The `free()` that happens in the `std::unique_ptr` destructor will be against stack memory, which you can't free. That's okay to demonstrate bad code, but contextually, it's incorrect. You should allocate bar. `bar * obj = new bar();`, then pass the reference `foo(*obj);`. On return that was freed, so `obj->num;` is undefined behavior. Also you may want to write that `std::cout << obj->num << "\n";`. – Alexis Wilke Nov 12 '19 at 08:05
  • 1
    @AlexisWilke, my example is for demostrating bad code, so I think it is ok, on the other hand, if I change my answer as you say (I'm agree your comment by the way) I wont be answering the OP's exact question, so, in my example I'm showing to the OP why his/her code could be broken . – Raydel Miranda Nov 12 '19 at 17:35
5

You can only assign another unique_ptr or the nullptr. If you think about it, this makes sense, too (though reset will let you do what you want, but I think this is actually a bug or deficiency in unique_ptr).

A unique_ptr is the exclusive owner of the pointed-to object. When it goes out of scope, it will delete the object.
This means that your function has sink semantics. The pointer you pass in (or rather the pointed-to object") is consumed, that is, it "disappears" (sinks) inside the function. You pass in an object by reference (an object which is not even necessarily heap-allocated, prepare for a surprise if you pass in an object with automatic storage!) and suddenly it's gone. Bang.

Sink semantics should be communicated properly. You should pass a unique_ptr as function parameter. Unique pointers cannot be copied, so this will force the user of that function to use std::move, creating awareness of what is acutally happening.

Having an object "disappear" is a nasty surprise, this shouldn't just happen unintentionally.

Damon
  • 61,669
  • 16
  • 122
  • 172
4

You could call std::unique_ptr::reset:

pointer.reset(&object);

but the real question is: Is this really doing what you expect it to do? If the object was not created via new, the above can be quite dangerous. Given that you haven't provided more information, you might have a valid use-case - but without this information it seems like a possible source of future trouble. What you effectively do in your example is that you consume the object, i.e., after the function has been called the lifetime of the unique_ptr ended and the object has been deleted. If this is documented/clear to the caller, it might be OK - otherwise rethink the design. And even if this is intended design, it is much better to use a unique_ptr as the argument of the function itself:

void foo(std::unique_ptr<bar> pointer)
{
    // ...
}

This does two things:

  • It communicates to the caller that the function will take ownership of the passed object.
  • It prevents resource leaks.
Daniel Frey
  • 52,317
  • 11
  • 110
  • 168
  • Thank you, that's exactly what I wanted to do. – Tomasz Kasperczyk Feb 13 '15 at 14:55
  • 3
    @user3125731: Are you sure? This will delete the object when the pointer goes out of scope, which will be disastrous if it wasn't created with `new`, or if anything else tries to delete it, or if the caller of the function doesn't realise that it silently deletes the function's argument. If you want the function to take ownership, it would be much clearer to take a smart pointer to indicate that. – Mike Seymour Feb 13 '15 at 15:03
  • @MikeSeymour Thanks for the hint, I updated the answer to show your argument to the OP in real code. – Daniel Frey Feb 13 '15 at 15:09
3

Allow me to disappoint you, but you don't. You have there a reference, which can point to either a dynamically allocated object or a stack allocated object, or, maybe, an object in a dynamically allocated array, which was not individually allocated.

You want to place its address in a class that will automatically call delete on that address. Which is invalid for most situations presented above. That is wrong. So passing a reference and putting the address of the reference in a smart pointer should never be done, ever. Especially not with reset().

At most, what you may want to do is initialize the smart pointer with the newly allocated object, for example:

auto ptr = std::unique_ptr<X>(new X(p1, p2));

Do not ever take the address of a reference. Ever. For no reason. It's morally wrong. It's not because the use of reference addresses is invalid, because it can be valid use; it's because two months later the new colleague will want to use that address with a smart pointer because that's how (s)he heard it's done nowadays, and then come to Stack Overflow and read that they should use „reset” for that pointer. Which is wrong, painfully wrong.

Dorin Lazăr
  • 666
  • 7
  • 7
  • It is morally and painfully wrong. But the question is not about correctness it is about "how". The answer states that doing that is not recommended. In the spirit of StackOverflow, you must not say an OP "You can't do that ..." while indeed there is a way to do it. If that way is bad, well you can advise him/her about the badness of some solution. In my years' programming, I've found more that one scenario where I had to use a not so recommended solution. It is nice to have the resource, it is nice to know. – Raydel Miranda Oct 22 '18 at 14:29
  • I can respect the fact that some wrong solution might be a solution nonetheless. If the OP got out of his question the idea that it's very wrong to do what they try to do, then I'm happy. The OP was on the wrong track, and I tried my best to help them avoid evil paths. For every bad solution someone gave for a bad question I asked, I would've appreciated a thousand times more an answer that would tell me that I'm on the wrong path. This is what I've done here. – Dorin Lazăr Oct 23 '18 at 15:14
  • Good enough, I think this thread is richer with your answer in place too. I understand your point and think you're right. Now the OP knows the way to do it and why he/her should not. – Raydel Miranda Oct 23 '18 at 17:49
1

The function you're looking for is reset(). Here's an example:

pointer.reset(&object);

Here pointer is a unique_ptr. Calling reset() will destroy the previous object managed by pointer (if there was any), and make object the current managed object of pointer.

Or, if you want to make object the managed object right when you initialize the unique_ptr:

std::unique_ptr<bar> pointer(&object);

A word of warning though: object should be allocated with new if you're going to manage it by a unique_ptr, because unique_ptr may try to call delete on it. If it was not created with new don't wrap it in a unique_ptr, that's not what they're for.

emlai
  • 37,861
  • 9
  • 87
  • 140
  • 4
    This will force the code through the compiler, but it needs to come with a dire warning. If the object wasn't created with `new`, or anything else tries to delete it, then this is horribly broken, and there's no way to enforce those requirement. – Mike Seymour Feb 13 '15 at 14:59
  • @MikeSeymour True added that to my answer. – emlai Feb 13 '15 at 15:05
1

What I can see is, you want to have unique_ptr object, which points to some object in memory,but you don't want to transfer the ownership of the memory.

You need to create custom deleter for the object, which actually does not delete the object:

class bar{};
struct barfakedeleter
{
   void operator()(bar*){/* do nothing here*/}
};

unique_ptr is a template where the second argument is the deleter of the object. By default it is default_deleter, but you can exchange it with this fake one:

void foo(bar& object) 
{
unique_ptr<bar,barfakedeleter> pointer(&object);
....
}

post edit: Same you can do with shared_ptr, but because shared_ptr can be cast to other shared_ptr within inheritance tree of the object, the deleter is not stored in class template argument, but it is past as instance member. This actually makes it easier to use IMO:

void foo(bar& object)
{
   shared_ptr<bar> pointer(&object,[](bar*){}); // just use anonymous function, which will not delete the object
}

You can do some custom release of resources as well:

shared_ptr<ObjectClass> pointer(GetObject(),[](ObjectClass* obj){ ReleaseTheObject(obj);});
Milan
  • 131
  • 6