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;
}
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 move
d 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::move
s).
#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
Since you explicitly asked about std::shared_ptr
,
here's also a demo with it: Live demo with std::shared_ptr.