How to assign the address of an existing object to

2020-06-30 04:34发布

问题:

#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.

回答1:

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?.



回答2:

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.



回答3:

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.


回答4:

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.



回答5:

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.



回答6:

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);});