Search code examples
c++shared-ptr

passing a shared_ptr to a function from another function


I'm returning to C++ after a long time, having inherited a code-base, a part of which has the structure in the following code. I have two objects, Wrapped and Wrapper and as might be expected, Wrapper wraps Wrapped. There are two factory methods make_Wrapper and make_Wrapped which return pointers to corresponding objects.

In the following code, I have observed that Case1 in which I use a shared_ptr does not work, whereas Case2 in which I use a normal C++ pointer works. By "does not work", I mean that the value nz_=10 in wrapped is replaced by junk in Case1 whereas it is equal to 10 in Case2. I can check this by printing the value of wrapper.nz_ in main via outside_wrapper->print().

I think this is because the object pointed to by the shared_ptr is deleted after this function make_Wrapper exits. Am I correct?

What is the right way to pass a object via shared_ptr from make_Wrapper to the Wrapper constructor?

#include <iostream>
#include <memory>
#include <functional>

// this is struct which is to be wrapped
struct Wrapped {
  int nz_;
  Wrapped(int nz):nz_(nz) {}
  inline virtual void destroy() { delete this; };
};

// this is the wrapper
struct Wrapper {
  Wrapper(Wrapped& wrapped):wrapped_(wrapped){}
  Wrapped& wrapped_;
  
  inline virtual void destroy() { delete this; };
  
  void print() {
    std::cout << "from wrapper: wrapped_.nz_=" << wrapped_.nz_<<std::endl;
  }
};

// factory function which returns pointer to wrapped object
Wrapped* make_Wrapped(int nz) {
  return new Wrapped(nz);
}


// factory function which returns pointer to wrapper object
Wrapper* make_Wrapper(int nz) {

  // Case 1
  // following line does not work - I think the object pointed to by the shared_ptr is deleted after this function exits
  std::shared_ptr<Wrapped> wrapped(make_Wrapped(nz), std::mem_fun(&Wrapped::destroy));

  // Case 2
  // following line works
  // Wrapped *wrapped = new Wrapped(nz);
  
  std::cout<<" from make_Wrapper "<<(*wrapped).nz_<<std::endl;
  return new Wrapper(*wrapped);
}

int main() {
    int nz=10;
    char tempbuffer[80];
    
    std::shared_ptr<Wrapper> outside_wrapper(make_Wrapper(nz), std::mem_fun(&Wrapper::destroy));
    outside_wrapper->print();

    std::cout << "Enter something to continue ..." << std::endl;
    std::cin >> tempbuffer;
    return 0;
}


Solution

  • You can use a smart pointer to hold the Wrapped object inside the Wrapper. When you use smart pointers, there's no need for raw pointers and manual new/delete. There's also no need (at least by default) for an explicit destroy() method.

    In the example below I used std::unique_ptr, which does not support copying, and is therefore moved instead when needed.
    std::unique_ptr should be the default smart pointer to use when it is sufficient.

    If you want to have shared ownership, you can use std::shared_ptr and std::make_shared instead (and then you can simply remove the std::moves).

    #include <iostream>
    #include <memory>
    
    // this is struct which is to be wrapped
    struct Wrapped {
        int nz_;
        Wrapped(int nz) :nz_(nz) {}
    };
    
    // this is the wrapper
    struct Wrapper {
        Wrapper(std::unique_ptr<Wrapped> wrapped) :wrapped_(std::move(wrapped)) {}
        std::unique_ptr<Wrapped> wrapped_;
    
        void print() {
            std::cout << "from wrapper: wrapped_.nz_=" << wrapped_->nz_ << std::endl;
        }
    };
    
    // factory function which returns pointer to wrapped object
    std::unique_ptr<Wrapped> make_Wrapped(int nz) {
        return std::make_unique<Wrapped>(nz);
    }
    
    // factory function which returns pointer to wrapper object
    std::unique_ptr<Wrapper> make_Wrapper(int nz) {
        auto wrapped = make_Wrapped(nz);
        std::cout << " from make_Wrapper " << wrapped->nz_ << std::endl;
        auto wrapper = std::make_unique<Wrapper>(std::move(wrapped));
        return wrapper;
    }
    
    int main() {
        int nz = 10;
        auto outside_wrapper = make_Wrapper(nz);
        outside_wrapper->print();
    }
    

    Output:

     from make_Wrapper 10
    from wrapper: wrapped_.nz_=10
    

    Live demo

    Since you explicitly asked about std::shared_ptr,
    here's also a demo with it: Live demo with std::shared_ptr.