Reputation: 179422
I'm trying to allow a class to contain a pointer, which may either be an owned pointer or a borrowed pointer. In the former case, it should destroy the owned object itself; in the latter case, it shouldn't destroy the pointed-to object.
In code, I have classes A, B and C. I'm aiming for the following (simplified) definitions, where B is the class that needs to own a pointer:
class C {
...
};
class B {
C *c;
B(C *c) : c(c) {
}
};
class A {
C c1;
B b1, b2;
// b2 leaks pointer to C
A() : b1(&c1), b2(new C()) {
}
};
When an instance of A
destructs, it destroys c1
, b1
and b2
. Ideally, the destruction of b2
should delete the anonymous C
instance, but the destruction of b1
should not delete anything (since c1
will be destroyed by A directly).
What kind of smart pointer can I use to achieve this? Or, is the best solution just to pass an ownership flag to B?
Upvotes: 6
Views: 1873
Reputation: 8977
Pass in a unique_ptr
via std::move
for the owned version and pass in a reference for the unowned version:
unique_ptr
#include <iostream>
#include <memory>
class C
{
public:
~C() { std::cout << "Goodbye\n"; }
void SayHello() { std::cout << "Hello\n"; }
};
class B
{
std::unique_ptr<C> owned;
C* unowned;
public:
B(C& c) : owned(nullptr)
, unowned(&c)
{ }
B(std::unique_ptr<C> c) : owned(std::move(c))
, unowned(owned.get())
{ }
C& GetC() { return *unowned; }
};
int main()
{
C stackC;
std::unique_ptr<C> heapC(new C);
B b1(stackC);
B b2(std::move(heapC));
b1.GetC().SayHello();
b2.GetC().SayHello();
}
OUTPUT:
Hello
Hello
Goodbye
Goodbye
Upvotes: 1
Reputation: 832
There is no way to archive this behavior without side effects, as far as I know. If it is just usual pointers (not COM), than you can access C via shared_ptr in both classes. If only B owns C, than they both will be destroyed with B's destroy. If both A & B owns C, than C will be destoyed only when last remaining alive owner (be it A or B) will be destroyed.
I know such practice to think about ownership: If method gets just a normal pointer, than it is meant that pointer will be used only inside that method. So, B will be:
class B1 {
B(C *c) {
//do some staff with c
}
void doSomeStaff(C*) {}
};
Or using & (cleaner, if your framework accept it):
class B2 {
B(C& c) {
//do some staff with c
}
void doSomeStaff(C&) {}
};
If method gets a shared pointer, it need this pointer for future reuse (keep it):
class B3 {
public:
std::shared_ptr<C> c;
B(std::shared_ptr<C> c) : c(c) {
}
};
So, now you can call b1.doSomeStaff(b3.c) or b2.doSomeStaff(*b3.c) without thinking who must destroy the pointed object C. You know only, that this object will be used in b1. That's all.
Do not forget to specify that you need shared_ptr, not C* in method - shared_ptr is an object, which increments reference count to object when copied. And not increments, but creates a new shared_ptr with reference count = 1, when constructed from C*.
This is not the answer to your question, but some of the common uses. See unique_ptr in Deduplicator's in answer. Also check: http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/smart_ptr.htm. Even if you don't use boost, there is a good theory for using different approaches to hold objects. Also check this answer: What is a smart pointer and when should I use one?
Upvotes: 0
Reputation: 45654
If you are sure and can guarantee that the reused C
will not be destroyed early (triple check that), there are multiple ways to go about it.
Some you might consider:
You can manually manage the pointer and a flag. Make sure you get the copy-semantic right, e.g. like this:
class B {
std::unique_ptr<C> c;
bool shared = false;
B(C& c) : c(&c), shared(true) {}
B(C *c = 0) : c(c) {}
~B() { if (shared) c.release(); }
};
You could use a custom deleter, like this:
template <class T> struct maybe_delete
{
void operator()(T* p) const noexcept {if(!shared) delete p;}
bool shared = false;
};
template <class T> struct maybe_delete<T[]>
{
void operator()(T* p) const noexcept {if(!shared) delete [] p;}
template <class U> void operator()(U*) const = delete;
bool shared = false;
};
class B {
std::unique_ptr<C, maybe_delete> c;
B(C& c) : B(&c) {this->c.get_deleter().shared = true;}
B(C *c) : c(c) {}
};
std::shared_ptr
, though that is probably severe overkill and might have too much overhead for you.Upvotes: 2
Reputation: 206577
While I fear for the potential abuse that B
is open to, you could do this:
class B {
C *c;
bool owned;
B(C& c) : c(&c), owned(false) {}
B(C *c) : c(c), owned(true) {}
~B() { if (owned) delete c; }
};
class A {
C c1;
B b1, b2;
A() : b1(c1), b2(new C()) {}
};
Upvotes: 1