简体   繁体   中英

How to mix C++ shared pointers and object references

I have the following code that uses a C++ object of class Datum within an Objective-C function work :

void work(const Datum &datum) {
    dispatch_async(dispatch_get_main_queue(), ^{
        // Work with datum.
    });
}

This code is called with an instance that is actually a boost::shared_ptr<Datum> , ie

boost::shared_ptr<Datum> the_datum(new Datum());
work(*the_datum);

In this situation it is possible that the instance the_datum gets deallocated before the block is run inside work (the call to dispatch_async performs an asynchronous operation on datum that is executed later; the call and thus the function work return immediately). This obviously results in a disaster.

One solution might be to not pass a reference to work , but a boost::shared_ptr<Datum> , instead. But there may be situations when references are preferred, see eg this thread . Is there any way to keep the interface of work (ie, pass datum as a reference), but still prevent the deallocation of the shared pointer prior to the completion of the block?

There is no way to accomplish this by leaving the interface to work() the same, and pass in a reference to datum . There is no way to use the datum to prevent the reference count from decrementing. Consider the following buggy program:

#include <memory>
int main () {
    std::shared_ptr<int> p1(new int);
    int &x = *p1;
    std::shared_ptr<int> p2(&x);
}

This code crashes with a double free, because the shared_ptr<> control structure does not follow the pointer to the object, but is followed through shared_ptr<> itself.

What you can do is change work() to take a shared_ptr() , but add some more code to the block passed to dispatch_async() so that it can use a reference within that code. Since you are transferring ownership to the asynchronous routine, you should use unique_ptr<> instead. I know zilch about Objective-C, so this syntax might be wrong:

void work(std::unique_ptr<Datum> &datumptr_ref) {
    __block std::unique_ptr<Datum> datumptr(std::move(datumptr_ref));
    dispatch_async(dispatch_get_main_queue(), ^{
        Datum &datum = *datumptr
        // Work with datum.
    });
}

You have to manage the references yourself:

#include <iostream>
#include <memory>

extern "C" {
    typedef struct TagShared Shared;
    Shared* allocate(const std::shared_ptr<int>& ptr) {
        return (Shared*) new std::shared_ptr<int>(ptr);
    }
    void deallocate(Shared* shared) {
        delete (std::shared_ptr<int>*)shared;
    }
 } // extern "C"

int main()
{
    std::shared_ptr<int> s(new int(1));
    Shared* p = allocate(s);
    // Inside Dispatch
        // No C++ but similar defined for TagShared
        std::cout << **(std::shared_ptr<int>*)p << std::endl;
        deallocate(p);
    return 0;
}

You want to transfer ownership with std::unique_ptr something like this:

void work(std::unique_ptr<Datum> datum) {
    dispatch_async(dispatch_get_main_queue(), ^{
        // Work with datum.
    });
}

std::unique_ptr<Datum> the_datum(new Datum());
work(std::move(the_datum));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM