1

Is there any significant difference in performance, memory, etc, between:

  • #1: moving a pointer to a temporary pointer, moving it back, then deleting the temporary pointer
  • #2: copying a pointer to a temporary pointer, then deleting the temporary pointer

I have the following code, two pointer of objects a Base and a Derived (which is derived from Base) are allowed to be stored inside a vector of pointers of Base objects, when reading the vector I need to check whether I need to dynamic_pointer_cast the pointer so data doesn't get sliced off.

#include "Base.h"
#include "Derived.h"

class Base
{
public:
    Base() {};
    ~Base() {};
};

class Derived: public Base
{
public:
    Derived() {};
    ~Derived() {};
};

int main()
{
    std::vector<std::shared_ptr<Base>> vectorOfBaseObjects;

    std::shared_ptr<Base> base = std::make_shared<Base>();
    std::shared_ptr<Derived> derived = std::make_shared<Derived>();

    vectorOfBaseObjects.push_back(base);
    vectorOfBaseObjects.push_back(derived);

    for (auto &it : vectorOfBaseObjects) {
        // #1: Move pointer to a temporary location and move it back when done
        if (std::shared_ptr<Derived> tmp_ptr = std::move(std::dynamic_pointer_cast<Derived>(it))) {
            // Do something with the derived object
            it = std::move(tmp_ptr);
        }

        // #2: Create a new temporary pointer
        if (std::shared_ptr<Derived> tmp_ptr = std::dynamic_pointer_cast<Derived>(it)) {
            // Do something with the derived object
        }
    }
}

Both statements work just fine, the only issues I could make of might be

  • #1: missing pointer locations in multi threaded appications in very rare cases, which could become a problem.
  • #2: an additional location assigned in the memory, which shoulnd't be an issue at all.
AlexG
  • 4,451
  • 5
  • 21
  • 43
  • 1
    `dynamic_pointer_cast` returns by value, so there is no difference. – tkausl Nov 29 '19 at 11:20
  • Have you tried to examine an actual assembly for both options? I assume compiler is smart enough to optimize out most of this code and end up with regular pointers, at least on certain high enough optimization levels. – Denis Sheremet Nov 29 '19 at 11:23
  • 6
    Also note "I need to check whether I need to dynamic_pointer_cast the pointer so data doesn't get sliced off." is flat wrong. The `Derived` object isn't the thing being copied or moved, there is no possibility of slicing. – Caleth Nov 29 '19 at 11:25
  • 1
    @Caleth "Note that `it = std::move(tmp_ptr);` is pointless." I thought the same at the beginning, though if I don't do that I cannot acces the pointer in the vector anymore after completing the loop. - "there is no possibility of slicing." Ah alrighty, good to know! – AlexG Nov 29 '19 at 11:29
  • No, you **haven't** modified `it`. If you had instead `std::dynamic_pointer_cast(std::move(it))` you would need to move it back in – Caleth Nov 29 '19 at 11:33
  • You really need to distinguish pointers from the things that they point to – Caleth Nov 29 '19 at 11:34
  • 1
    The first version seems to defeat part of the purpose of having a `shared_ptr` to me. You reintroduce the potential for bugs that `shared_ptr` should protect against. – Galik Nov 29 '19 at 11:35
  • The first one will leave `it` empty if an exception is thrown in between. – user253751 Nov 29 '19 at 11:47
  • 3
    @user253751, that's untrue, because `std::dynamic_pointer_cast()` returns a *new* shared pointer, and doesn't change `it`. The `std::move()` achieves nothing there, since it's acting on an rvalue. – Toby Speight Nov 29 '19 at 11:56
  • @TobySpeight Ah perfect that makes sense! That also explains Caleths most recent comment! – AlexG Nov 29 '19 at 11:59

2 Answers2

3

The two cases are pretty much equivalent, since std::dynamic_pointer_cast() returns a new shared pointer. it is not moved from in this expression:

std::move(std::dynamic_pointer_cast<Derived>(it))

The result of the cast is already an xvalue, so that's exactly the same as

std::dynamic_pointer_cast<Derived>(it)

The only difference is the copy of the pointer back to it. If you've not changed what it points to, then that's a wasted statement.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
1

If you're worried about the speed of creating new shared_ptrs then tmp_ptr probably doesn't even need to be a shared_ptr. Using a raw pointer would look like this:

Derived* tmp_ptr = dynamic_cast<Derived*>(it.get());
Alan Birtles
  • 22,711
  • 4
  • 22
  • 44