Home > Software design >  Smart pointers still refers to raw pointer even though reset is applied
Smart pointers still refers to raw pointer even though reset is applied

Time:08-13

Please find the code attached which I took from https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/ for testing the

// C   program to demonstrate shared_ptr
#include <iostream>
#include <memory>

class A {
public:
    void show()
    {
        std::cout << "A::show()" << std::endl;
    }
};

int main()
{
    std::shared_ptr<A> p1(new A);
    std::cout << p1.get() << std::endl;
    p1->show();
    std::shared_ptr<A> p2(p1);
    p2->show();
    std::cout << p1.get() << std::endl;
    std::cout << p2.get() << std::endl;

    // Returns the number of shared_ptr objects
    // referring to the same managed object.
    std::cout << p1.use_count() << std::endl;
    std::cout << p2.use_count() << std::endl;

    // Relinquishes ownership of p1 on the object
    // and pointer becomes NULL
    p1.reset();
    std::cout << p1.get() << std::endl;
    std::cout << p2.use_count() << std::endl;
    std::cout << p2.get() << std::endl;
    p1->show();
    p2->show();
    std::cout << p1.get() << std::endl;
    std::cout << p2.use_count() << std::endl;
    std::cout << p2.get() << std::endl;

    return 0;
}

p1 is reset and no pointer is assigned to it (Found as per p1.get()). After that, When I am calling p1->show() function, it shows the output as A::show(). How it is possible?

output:
0x24dc5ef1790
A::show()
A::show()
0x24dc5ef1790
0x24dc5ef1790
2
2
0
1
0x24dc5ef1790
A::show() 
A::show()
0
1
0x24dc5ef1790

CodePudding user response:

It works because you are not using any member from the object otherwise it would segfault. But a sanitizer catches this very easily:

$ g   -ggdb -O0 -fsanitize=undefined,address shared.cpp -o shared
$ ./shared
0x602000000010
A::show()
A::show()
0x602000000010
0x602000000010
2
2
0
1
0x602000000010
shared.cpp:33:13: runtime error: member call on null pointer of type 'struct element_type'
A::show()
A::show()
0
1
0x602000000010

Change the code to add one member variable to print like

class A {
public:
    void show()
    {
        std::cout << "A::show() " << value << std::endl;
    }
    int value;
};

And it segfaults

$ g   -ggdb -O0 shared.cpp -o shared
$ ./shared
0x560a3dde4eb0
A::show() 0
A::show() 0
0x560a3dde4eb0
0x560a3dde4eb0
2
2
0
1
0x560a3dde4eb0
Segmentation fault (core dumped)

CodePudding user response:

In your example, the show() function doesn't use any member variables, therefore it doesn't need this pointer. If we bear in mind that non-static member functions implicitly called with an additional this pointer, using non-static member variables in show() will require a this pointer. So it is obvious that if you use member variables in show, your example can crash, because the this pointer passed in will be nullptr in the case of calling resetted pointer.

Non-static member functions are called like this:

A::show(A* this)

Say that A has a member variable called id, and we print it in show()

A a;
a.show();

This converts to:

A::show(A*this)
{
  std::cout << "Id: " << this->id << " A::show()" << std::endl;
}

It enables an oppurtunity like calling the show() with nullptr:

std::invoke(&A::show, (A*)nullptr);

Unless you use nulled this pointer here, there is no reason for your program to crash. But how does p2 know which function to call? The answer is simple. Shared pointers are template classes, therefore when you instantiate it, it already knows your class A. Then it can call it functions supplying the raw pointer it stores. Doesn't matter if the raw pointer is nullptr.

But you shouldn't rely on that. It is undefined behaviour.

  • Related