whoosy
whoosy

Reputation: 297

Is there any way to check if an std::function points to a member of a valid object?

Let me demonstrate what I mean.

#include <functional>
#include <iostream>

class MyClass
{
private:
    int number;
public:
    MyClass()
    {
        number = 0;
    }

    void printNumber()
    {
        std::cout << "number is " << number << std::endl;
        number++;
    }
};

int main()
{
    std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();
    auto function = std::bind(&MyClass::printNumber, obj);

    function();

    // This deallocates the smart pointer.
    obj.reset();

    // This shouldn't work, since the object does not exist anymore.
    function();

    return 0;
}

This outputs:

number is 0
number is 1

If we were to call the function like we would normally, replacing "function()" with "obj->printNumber()", the output is:

number is 0
Segmentation fault: 11

Just like you would expect it to be.

So my question is if there is any way to make sure that we are unable to call the function when the object has been deallocated? This has nothing to do with using smart pointers, it works the same way with regular pointers.

Upvotes: 0

Views: 1616

Answers (3)

quantdev
quantdev

Reputation: 23793

Is there any way to check if an std::function points to a member of a valid object?

std::function does not point to a "member of an object", it encapsulates a Callable. When you bind it with an object, you get another callable object (of unspecified type actually), that can be stored in an std::function

So my question is if there is any way to make sure that we are unable to call the function when the object has been deallocated?

No, there is no way. But as noted by ecatmur, since std::bind copies it's argument(s), you are fine in your example.

Upvotes: 3

Slava
Slava

Reputation: 44258

"This has nothing to do with using smart pointers, it works the same way with regular pointers." This statement is incorrect. obj->printNumber() with smart pointer fails not because object is destroyed, but because this particular instance of shared pointer obj does not point to a valid object anymore. If you try to do this with raw pointers:

MyClass *obj = new MyClass;
auto function = std::bind(&MyClass::printNumber, obj);

function();

delete obj;

// This will have UB
function();

you will get completely different situation (undefined behavior) of using an object after destruction and memory deallocation.

There is no way in C++ to validate that raw pointer points to a valid object. But what you can use is a std::weak_ptr. It will not work directly with std::bind so you may want to write a lambda or a simple wrapper, where you would check that object is still valid:

auto wrapper = []( std::weak_ptr<MyClass> wobj ) { auto obj = wobj.lock(); if( obj ) obj->printNumber(); };
auto function = std::bind( wrapper, std::weak_ptr<MyClass>( obj ) );

Upvotes: 0

ecatmur
ecatmur

Reputation: 157364

Your code is valid.

obj.reset() does not deallocate the shared MyClass object, because a copy of the shared pointer obj exists within the bind-expression function. This is because std::bind copies (or moves) its arguments, unless wrapped in reference_wrapper (e.g. by std::ref).

If you instrument or place a breakpoint on the destructor of MyClass, you will find it is not called until the end of main, when the local variable function is destructed.

Upvotes: 6

Related Questions