unique_ptr heap and stack allocation

2020-05-20 09:12发布

Raw pointers can point to objects allocated on the stack or on the heap.

Heap allocation example:

// heap allocation
int* rawPtr = new int(100);
std::cout << *rawPtr << std::endl;      // 100

Stack allocation example:

int i = 100;
int* rawPtr = &i;
std::cout << *rawPtr << std::endl;      // 100

Heap allocation using auto_ptr example:

int* rawPtr = new int(100);
std::unique_ptr<int> uPtr(rawPtr);
std::cout << *uPtr << std::endl;        // 100

Stack allocation using auto_ptr example:

int i = 100;
int* rawPtr = &i;
std::unique_ptr<int> uPtr(rawPtr);      // runtime error

Are 'smart pointers' intended to be used to point to dynamically created objects on the heap? For C++11, are we supposed to continue using raw pointers for pointing to stack allocated objects? Thank you.

4条回答
兄弟一词,经得起流年.
2楼-- · 2020-05-20 09:38

Are 'smart pointers' intended to be used to point to dynamically created objects on the heap?

They are intended for heap-allocated objects to prevent leaks.

The guideline for C++ is to use plain pointers to refer to a single object (but not own it). The owner of the object holds it by value, in a container or via a smart pointer.

查看更多
老娘就宠你
3楼-- · 2020-05-20 09:49

The runtime error is due to the fact that delete was called on a memory location that was never allocated with new.

If an object has already been created with dynamic storage duration (typically implemented as creation on a 'heap') then a 'smart pointer' will not behave correctly as demonstrated by the runtime error.

Are 'smart pointers' intended to be used to point to dynamically created objects on the heap? For C++11, are we supposed to continue using raw pointers for pointing to stack allocated objects?

As for what one is supposed to do, well, it helps to think of the storage duration and specifically how the object was created.

  • If the object has automatic storage duration (stack) then avoid taking the address and use references. The ownership does not belong with the pointer and a reference makes the ownership clearer.
  • If the object has dynamic storage duration (heap) then a smart pointer is the way to go as it can then manage the ownership.

So for the last example, the following would be better (pointer owns the int):

auto uPtr = std::make_unique<int>(100);

The uPtr will have automatic storage duration and will call the destructor when it goes out of scope. The int will have dynamic storage duration (heap) and will be deleteed by the smart pointer.

One could generally avoid using new and delete and avoid using raw pointers. With make_unique and make_shared, new isn't required.

查看更多
闹够了就滚
4楼-- · 2020-05-20 09:52

Smart pointers are usually used to point to objects allocated with new and deleted with delete. They don't have to be used this way, but that would seem to be the intent, if we want to guess the intended use of the language constructs.

The reason your code crashes in the last example is because of the "deleted with delete" part. When it goes out of scope, the unique_ptr will try to delete the object it has a pointer to. Since it was allocated on the stack, this fails. Just as if you had written, delete rawPtr;

Since one usually uses smart pointers with heap objects, there is a function to allocate on the heap and convert to a smart pointer all in one go. std::unique_ptr<int> uPtr = make_unique<int>(100); will perform the actions of the first two lines of your third example. There is also a matching make_shared for shared pointers.

It is possible to use smart pointers with stack objects. What you do is specify the deleter used by the smart pointer, providing one that does not call delete. Since it's a stack variable and nothing need be done to delete it, the deleter could do nothing. Which makes one ask, what's the point of the smart pointer then, if all it does is call a function that does nothing? Which is why you don't commonly see smart pointers used with stack objects. But here's an example that shows some usefulness.

{
    char buf[32];
    auto erase_buf = [](char *p) { memset(p, 0, sizeof(buf)); };
    std::unique_ptr<char, decltype(erase_buf)> passwd(buf, erase_buf);

    get_password(passwd.get());
    check_password(passwd.get());
}
// The deleter will get called since passwd has gone out of scope.
// This will erase the memory in buf so that the password doesn't live
// on the stack any longer than it needs to.  This also works for
// exceptions!  Placing memset() at the end wouldn't catch that.
查看更多
倾城 Initia
5楼-- · 2020-05-20 09:59

Are 'smart pointers' intended to be used to point to dynamically created objects on the heap?

Yes, but that's just the default. Notice that std::unique_ptr has a constructor (no (3)/(4) on that page) which takes a pointer that you have obtained somehow, and a "deleter" that you provide. In this case the unique pointer will not do anything with the heap (unless your deleter does so).

For C++11, are we supposed to continue using raw pointers for pointing to stack allocated objects? Thank you.

You should use raw pointers in code that does not "own" the pointer - does not need to concern itself with allocation or deallocation; and that is regardless of whether you're pointing into the heap or the stack or elsewhere.

Another place to use it is when you're implementing some class that has a complex ownership pattern, for protected/private members.

PS: Please, forget about std::auto_ptr... pretend it never existed :-)

查看更多
登录 后发表回答