Question about smart pointers

I recently read LBs article on smart pointers. ( http://www.lb-stuff.com/pointers )
Again, I'm left with a question that none of all the articles that I've read about smart pointers answered.
(@LB: good article, though)

The question is:
Are smart pointers faster (both in execution and allocation/deallocation) and/or less memory consuming than raw pointers?
Including runtime overhead.

LB talks about smart pointers being "better, safer, more self-documenting". I read that a lot, especially the 'safe' argument seems to be key.
I understand that point, but let's assume I don't care about 'safe' or 'ease of use' or 'readability' or 'maintainability', should I still use smart pointers and why?

A final note: I do use smart pointer in my daily work, so, I'm aware of all the advantages of them.
I just want this question answered.
Last edited on
Are smart pointers faster.

A little bit slower and consume a little bit more memory. But other than that, their own advantages are clearly remarkable comparing to normal pointers or raw pointers.

Sadly, you might often need to understand raw pointers (how pointers work in general) to a certain extent before you know which type of smart pointer you need to choose and use them proficiently.
Well, I presume that no smart pointer can be less than a raw pointer:
1
2
3
4
5
template <typename T>
class smart {
  T * raw;
  // other code
};

The question is thus, whether the "other code" increases the memory footprint and/or how well the compiler optimizes out the dereference operator, etc.

The binary must have code for the destructor and calls to it, but then again the properly written raw pointer source has delete, i.e. similar code.


It is thus practically impossible to be smaller or faster, but some smart pointers are very close. Some, for there are very different ways and reasons of being "smart".
Maybe, the compiler can compile all overhead away so that the machine code of the smart pointer is not to distinguish from a raw pointer.
std::unique_ptr with the default deleter typically has the same memory footprint and comparable performance characteristics as a raw pointer.

Memory footprint:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <memory>
#include <iostream>

struct A
{
    int v = 7 ;
};

int main()
{
    std::cout << "size of raw pointer: " << sizeof(A*) 
              << "    size of unique_ptr: " << sizeof( std::unique_ptr<A> ) << '\n' ;
}

clang++ -std=c++14 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors  main.cpp -lsupc++ && ./a.out 
echo && echo && g++ -std=c++14 -O3 -Wall -Wextra -pedantic-errors  main.cpp && ./a.out
size of raw pointer: 8    size of unique_ptr: 8


size of raw pointer: 8    size of unique_ptr: 8

http://coliru.stacked-crooked.com/a/13423ad1e85ed082

Performance:
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
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <memory>

struct A
{
    int v = 7 ;
};

int foo_raw( const A* pa ) 
{ 
    return pa ? pa->v : 0 ; 
    /*
        xorl    %eax, %eax
        testq   %rdi, %rdi
        je      .LBB0_2
        movl    (%rdi), %eax
    .LBB0_2:
        retq
    */
}

int foo_smart( std::unique_ptr<A> pa ) 
{ 
    return pa ? pa->v : 0 ; 
    /*
        movq    (%rdi), %rcx
        xorl    %eax, %eax
        testq   %rcx, %rcx
        je      .LBB1_2
        movl    (%rcx), %eax
    .LBB1_2:
        retq
    */ 
}

// most efficient if an object will always be passed
int foo_smarter( const A& a ) 
{ 
    return a.v ; 
    /*
        movl    (%rdi), %eax
        retq
    */
}

http://coliru.stacked-crooked.com/a/b986297f3edd4bf9

The general rules, IMNSHO:

If an object will always be present, use a reference or std::reference_wrapper (note: references are non-owning).

Otherwise, for a non-owning pointer, use a raw pointer.
eg. int std::stoi( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
There may not be an object pointed to by pos; it is evident that pos is a non-owning pointer.
If Library Fundamentals v2 is available, std::experimental::observer_ptr<> is a vocabulary option.
http://en.cppreference.com/w/cpp/experimental/observer_ptr
Note: avoid using std::optional<T> to simulate a raw pointer to T which may be null. (Value semantics are never a drop-in replacement for reference semantics.)

Otherwise, for an owning pointer (in high-level code), use a smart pointer. (Favour unique ownership over shared ownership.)
Thanks guys. That's the answer I was looking for.

I like the assembly listings. No better way to show what's actually going on.

So, I guess I don't really have to worry about my function that's called thousands of times per second and uses a bunch of local smart pointers.


Topic archived. No new replies allowed.