Raw and smart pointers

1
2
3
4
5
6
7
int* foo(int n) {
 return new int(n * 2);
}

int main() {
 std::cout << *foo(12);
}

Here we've a raw pointer that should be deleted before main() ends, right? How?

The second question is when we use a smart pointer that does that job for us:

1
2
3
4
5
6
7
std::unique_ptr<int> foo(int n) {
 return std::unique_ptr<int>{new int(n * 2)};
}

int main() {
 std::cout << *foo(12);
}

That unique pointer is scoped, and I assume it's inside the caller of foo, main(). So at the end of main() that unique pointer's destructor is called. Right?

The third question goes to the newer form, std::make_unique.
1
2
3
4
5
6
7
std::unique_ptr<int> foo(int n) {
 return std::make_unique<int>(n * 2);
}

int main() {
 std::cout << *foo(12);
}

What's the difference between this and the second code, especially in terms of pointer scope and memory deallocation?

Last edited on
Here we've a raw pointer that should be deleted before main() ends, right? How?
As the code is currently structured, you can't. It's a memory leak.

So at the end of main() that unique pointer's destructor is called. Right?
Well, almost. It's destroyed at the end of the code on line 6, since it's an unnamed temporary object. This is more evident if you have a destructor that actually does something.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

struct Inescutcheon {
    Inescutcheon(int n)
    : n(n)
    {
        std::cout << "Hi!\n";   
    }
    
    ~Inescutcheon()
    {
        std::cout << "Bye!\n";   
    }
    
    int n;
};

std::ostream& operator<<(std::ostream& os, const Inescutcheon& ine)
{
    return os << "(" << ine.n << ")";
}

std::unique_ptr<Inescutcheon> foo(int n) {
    return std::unique_ptr<Inescutcheon>{new Inescutcheon(n * 2)};
}

int main() {
    std::cout << *foo(12) << '\n';
    std::cout << "Oh hey, still in main!\n";
}
Hi!
(24)
Bye!
Oh hey, still in main!


std::make_unique.
make_unique is just a nicety so that the user doesn't have to type 'new' because it's icky.

Also, wtf, 1-space indents? 😲 🤮
Last edited on
make_unique is just a nicety so that the user doesn't have to type 'new' because it's icky.
Yes but another primary reason is exception safety:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3588.htm
Given
f(std::unique_ptr<int>(new int), g())
The compiler could evaluate new int and g() before constructing any unique_ptr to hold the results of new int. In this case if g throws a memory leak results.
Last edited on
I personally like the make functions for smart pointers as well as std::pair & std::tuple, using it 'self-documents' what I am doing. YMMV.

1-space indents?
My indents are 3 spaces, any tab(s) get converted to spaces on save.

4 is good, but IMO it can push code too far to the right. Even with a 1920x1080 display, the edit window is smaller, sometimes code goes hiding.

Even with a tab stop spacing of 3 on occasion I get scrolling code. Then I try to white-space it onto separate lines.

Tabs as tabs works, until code is pasted here and the white-space grows way too much. YMMV.
frek wrote:
Here we've a raw pointer that should be deleted before main() ends, right? How?

It should be deleted by the caller but it's not. This leads to a memory leak.

Correcting the caller's code it would look something like the following:

1
2
3
4
5
int main() {
 int* res = foo(12);
 std::cout << *res;
 delete res;
}

It's quite easy to forget or not even be aware of the fact that you have to use delete which is why it's usually a bad idea to return an owning raw pointer to dynamically allocated memory.

EDIT: The caller could use a smart pointer even if the function returns a raw pointer:

1
2
3
4
int main() {
 std::unique_ptr<int> res(foo(12));
 std::cout << *res;
}

In both cases you need to be absolutely sure that the function intends delete to be used on the returned pointer. You cannot assume a raw pointer means you should use delete. It might point to a static variable which is the case with for example std::localtime or it might have been allocated with malloc which means free should be used instead. C doesn't have destructors so if you're using a C library you might sometimes have to use special functions to destroy certain types of objects. One example of this is SDL_DestroyTexture in the SDL library.
https://wiki.libsdl.org/SDL2/SDL_DestroyTexture
Last edited on
Thank you all.
mbozzi wrote:
The compiler could evaluate new int and g() before constructing any unique_ptr to hold the results of new int. In this case if g throws a memory leak results.
Good to know, I wasn't aware of that peculiarity. Thanks for the example.
Last edited on
Topic archived. No new replies allowed.