Passing out shared pointer to this from destructor - c++

I have an object, BagOfThings, that stores a set of Things and a list of BagOfThingsListeners, which want to know when a Thing is added or removed from the BagOfThings they've been added to. Like this:
class Thing;
class BagOfThings;
class BagOfThingsListener {
public:
virtual ~BagOfThingsListener() {}
virtual void thingAdded(std::shared_ptr<BagOfThings> bag, std::shared_ptr<Thing> thing)=0;
virtual void thingRemoved(std::shared_ptr<BagOfThings> bag, std::shared_ptr<Thing> thing)=0;
};
class BagOfThings: public enable_shared_from_this<BagOfThings> {
private:
std::set<std::shared_ptr<Thing>> things;
std::list<std::shared_ptr<BagOfThingsListener>> listeners;
private:
BagOfThings() {}
public:
static std::shared_ptr<BagOfThings> create() {
return std::shared_ptr<BagOfThings>(new BagOfThings());
}
void addThing(std::shared_ptr<Thing> thing) {
things.insert(thing);
for (auto it=begin(listeners); it!=end(listeners); ++it) {
(*it)->thingAdded(shared_from_this(), thing);
}
}
void removeThing(std::shared_ptr<Thing> thing) {
things.erase(thing);
for (auto it=begin(listeners); it!=end(listeners); ++it) {
(*it)->thingRemoved(shared_from_this(), thing);
}
}
~BagOfThings() {
for (auto it=begin(things); it!=end(things);) {
auto currentIt=it++;
auto &currentThing=*currentIt;
things.erase(currentIt);
for (auto it2=begin(listeners); it2!=end(listeners); ++it2) {
(*it2)->thingRemoved(shared_from_this(), currentThing);
}
}
}
};
This works fine except for the destructor, which is invalid as you're not allowed to use shared_from_this() when all shared_ptrs have been destroyed, which they have by the time the destructor is called. In this case I'm using shared pointers, but it seems to me that handing out the this pointer from the destructor is problematic anyway - someone might store the pointer, for instance. But in this case (wanting to let listeners know on destruction of the removal of all elements), I can't see an obvious nice way of doing it, without removing the pointer to the caller from the listener (i.e. thingAdded would become void thingAdded(std::shared_ptr<Thing>)).
Any ideas?

Why does BagOfThingsListener::thingAdded and BagOfThingsListener::thingRemoved need to take a shared_ptr? Would not a reference/const reference to BagOfThings be enough? When BagOfThings calls thingAdded or thingRemoved, you know that the this pointer is valid, so a reference will thus also be valid.

Related

Removing an object from a singleton-managed container upon destruction

I have a Singleton class that manages a container of Items, exposing public functions that allow Items to be added or removed from the container.
class Item;
typedef std::shared_ptr<Item> ItemPtr;
class Singleton
{
public:
static Singleton& Instance()
{
static std::unique_ptr<Singleton> Instance(new Singleton);
return *Instance;
}
void Add(ItemPtr item)
{
mContainer.push_back(item);
}
void Remove(ItemPtr item)
{
for (auto it = mContainer.begin(); it != mContainer.end(); it++)
if (*it == item)
mContainer.erase(it);
}
private:
std::vector<ItemPtr> mContainer;
};
I'd like Item to have the ability to add itself to the Singleton container via an Add() method, and remove itself from the container upon its destruction.
class Item
{
public:
Item() {}
~Item()
{
Singleton::Instance().Remove(ItemPtr(this));
}
void Add()
{
Singleton::Instance().Add(ItemPtr(this));
}
};
When I run the example below, I get a crash on Singleton::Remove(), specifically a EXC_BAD_ACCESS on mContainer.begin().
int main()
{
Item* a = new Item();
Item* b = new Item();
a->Add();
b->Add();
delete a;
delete b;
}
This seems to indicate that mContainer no longer exists. Looking at the call stack, I can also see one of the root call stack frames is the destructor Singleton::~Singleton(), which would explain why mContainer is no longer there.
I've tried a different approach : instead of using std::shared_ptr<Item> I simply used raw pointers (i.e., Item*) with the appropriate substitutions in the code. It worked without problems.
My questions are:
I guess what's happening is that the ownership of the Item objects is only released by the shared_ptr after the destruction of Singleton, which causes the error. Is this correct?
Is it impossible to do what I want to do if the container in Singleton is of shared_ptr<Item>?
If not, how could I do it?
The wisdom of doing this in the first place notwithstanding, what you want can be achieved if you're willing to use, and abide by the restrictions of, std::enabled_shared_from_this. See below:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
struct Item;
typedef std::shared_ptr<Item> ItemPtr;
class Singleton
{
private:
Singleton() {}
public:
static Singleton &Instance()
{
static Singleton s;
return s;
}
void Add(ItemPtr item)
{
mContainer.emplace_back(std::move(item));
}
void Remove(const ItemPtr& item)
{
mContainer.erase(
std::remove(mContainer.begin(), mContainer.end(), item),
mContainer.end());
}
void Clear()
{
mContainer.clear();
}
private:
std::vector<ItemPtr> mContainer;
};
// note derivation. this means you can get a std::shared_ptr<Item>
// via `shared_from_this` , but it also means the object itself
// MUST be an actual shared object to begin with.
struct Item : public std::enable_shared_from_this<Item>
{
void Add()
{
Singleton::Instance().Add(shared_from_this());
}
};
int main()
{
ItemPtr a = std::make_shared<Item>();
ItemPtr b = std::make_shared<Item>();
// add to the singleton container
a->Add();
b->Add();
// report reference count of 'a'
std::cout << "before removal 'a' has " << a.use_count() << " references\n";
Singleton::Instance().Remove(a);
std::cout << "after removal 'a' has " << a.use_count() << " references\n";
}
Output
before removal 'a' has 2 references
after removal 'a' has 1 references
The most important part of this is the creation of a and b in main . Notice they are, in fact, managed by std::shared_ptr enshrouding from inception. This is required for std::enable_shared_from_this to work correctly. The rest is fairly straight forward. The ability to get a reference-bumped std::shared_ptr from within the body of any member of Item is done via the shared_from_this() member provided from the base class std::enable_shared_from_this.
In short, taking this approach will work for you, but at no point can you use shared_from_this() unless the object it is being fired upon is already managed by a std::shared_ptr in the first place. Keep that in mind.

Pointer returned by unique_ptr<T>::get() is not nullptr after original unique_ptr is destroyed

I have a global vector of unique_ptrs to a base class, to which I append unique_ptrs to a derived class:
std::vector<std::unique_ptr<Base>> global_vec;
template<typename T>
Base* create_object()
{
std::unique_ptr<T> uptr = std::make_unique<T>(/* ... */);
Base* last_ptr = uptr.get();
global_vec.emplace_back(std::move(uptr));
return last_ptr; /*this is a bit irrelevant, but is for the caller*/
}
Now, Base itself has a member vector of raw pointers to Base:
struct Base
{
...
std::vector<Base*> providers;
...
}
The pointers that make up Base::providers are all obtained via calling unique_ptr::get() from global_vec:
void Base::subscribe_to(Base* src)
{
providers.push_back(src);
}
Base has a member function that does work with these subscribers, and checks for nullptr before doing work:
void Base::do_work()
{
...
for(Base* ptr : providers)
{
if(ptr != nullptr)
{
...
}
}
}
Now, somewhere else in my code, I can erase the unique_ptrs in global_vec:
auto itr = std::find_if(global_vec.begin(), global_vec.end(), [&](std::unique_ptr<Base> const& n)
{ return n.get() == ptr_selected; }); //ptr_selected points to a widget selected by the user
global_vec.erase(itr);
However, after erasing an element, Base::suscribers will still hold a valid pointer to an object. That is, when iterating through Base::providers in Base::do_work(), no Base* will equal std::nullptr.
I would expect that erasing a unique_ptr from global_vec would invoke Base::~Base(), thus rendering the pointers in Base::providers as std::nullptr. Base's destructor is invoked, but the pointers are valid (you can even get access to data members from them).
Base does have a virtual destructor.
Why are the pointers in Base::providers still valid?
I would expect that erasing a unique_ptr from global_vec would invoke Base::~Base()
Yes, it does.
thus rendering the pointers in Base::providers as std::nullptr.
This is where your expectation fails. There is no way for raw pointers to an object to be set to nullptr automatically when the object is destroyed. It is YOUR responsibility to handle that manually in your own code. You need to remove a Base* pointer from the providers vector before/when the corresponding object is destroyed. The compiler cannot do that for you.
You might consider having two vector<Base*> in your Base class, one to keep track of objects that this has subscribed to, and one to keep track of objects that have subscribed to this. Then, ~Base() can unsubscribe this from active subscriptions, and notify active subscribers that this is going away. For example:
struct Base
{
...
protected:
std::vector<Base*> providers;
std::vector<Base*> subscribers;
...
public:
~Base();
...
void subscribe_to(Base* src);
void unsubscribe_from(Base* src);
...
};
Base::~Base()
{
std::vector<Base*> temp;
temp = std::move(providers);
for(Base* ptr : temp) {
unsubscribe_from(ptr);
}
temp = std::move(subscribers);
for(Base* ptr : temp) {
ptr->unsubscribe_from(this);
}
}
void Base::subscribe_to(Base* src)
{
if (src) {
providers.push_back(src);
src->subscribers.push_back(this);
}
}
void Base::unsubscribe_from(Base* src)
{
if (src) {
std::remove(providers.begin(), providers.end(), src);
std::remove(src->subscribers.begin(), src->subscribers.end(), this);
}
}
void Base::do_work()
{
...
for(Base* ptr : providers) {
...
}
...
}
...
std::vector<std::unique_ptr<Base>> global_vec;
Otherwise, consider using std::shared_ptr instead of std::unique_ptr in your global vector, and then you can store std::weak_ptr<Base> objects instead of raw Base* pointers in your other vectors. When you access a std::weak_ptr, you can query it to make sure the associated object pointer is still valid before using the pointer:
struct Base
{
...
protected:
std::vector<std::weak_ptr<Base>> providers;
...
public:
...
void subscribe_to(std::shared_ptr<Base> &src);
...
};
void Base::subscribe_to(std::shared_ptr<Base> &src)
{
if (src) {
providers.push_back(src);
}
}
void Base::do_work()
{
...
for(std::weak_ptr<Base> &wp : providers) {
std::shared_ptr<Base> ptr = wp.lock();
if (ptr) {
...
}
}
...
}
...
std::vector<std::shared_ptr<Base>> global_vec;
Base's destructor is invoked, but the pointers are valid
No, they are not valid, since the object being pointed to was destroyed. The pointers are simply left dangling, pointing to the old memory, and are not nullptr like you are expecting.
you can even get access to data members from them
It is undefined behavior to access members of an object after it has been destroyed.

Shared pointers do not intrinsically cast to base/derived types

What I did: I recently started to write a multi-threaded producer-consumer-style queue. Initially, I went with smart pointers but ended up changing all of them to raw pointers and managing their lifetime and their memory management manually (code at the end, if interested).
What I'm looking for: arguments for or against this conjecture:
Inheritance can't sit in the same room as shared pointers, as nicely as it would with raw pointers and reference objects.
My Reasoning:
Base and Derived objects are covariant. So are raw pointers (Base* and Derived*). Shared pointers (shared_ptr<Base> and shared_ptr<Derived>) are not.
The programmer has to do many conceptually unnecessary downcasting using dynamic_pointer_cast, which makes the code ugly and are somewhat expensive at compile time and run time.
This makes me wonder if shared pointers should be avoided in object-oriented designs, as their benefits do not overweigh their expense and headache.
My code before change (multi-threading is left out for the sake of readability):
typedef shared_ptr<Animal> animal_ptr;
typedef shared_ptr<Dog> dog_ptr;
class Buffer {
private:
mutex mu_;
condition_variable cond_;
deque<shared_ptr<Animal> > buffer_;
public:
void add(shared_ptr<Animal> req) {
std::unique_lock<std::mutex> locker(mu_);
cond_.wait(locker, [this](){return buffer_.size() < size_;});
buffer_.push_back(req);
locker.unlock();
cond_.notify_all();
}
shared_ptr<Animal> remove() {
unique_lock<mutex> locker(mu_);
cond_.wait(locker, [this](){return buffer_.size() > 0;});
shared_ptr<Animal> back = buffer_.back();
buffer_.pop_back();
locker.unlock();
cond_.notify_all();
}
};
int main() {
Buffer buffer;
animal_ptr bPtr1 (new Animal()); // buffer.add() works just fine
dog_ptr dPtr1 (new Dog()); // EDIT: works fine too.
animal_ptr dptr2 (new Dog()); // EDIT: it's okay
...
buffer.remove(); // returns a base class object, requires downcasting to access derived members
}
EDIT for more clarification, and why shared pointers are treating inheritence differently than raw pointers:
void func1(shared_ptr<Animal> ptr);
void func2(Animal* ptr);
...
Dog* rawPtr = new Dog();
func1(dPtr1); // is not possible, requires upcasting
func2(rawPtr); // is ok.
No, it works.
http://coliru.stacked-crooked.com/a/b2a83c740ed60521
#include <iostream>
#include <memory>
struct A {
void print() { std::cout << "A" << std::endl; }
};
struct B : public A {
void print() { std::cout << "B" << std::endl; }
};
void print(std::shared_ptr<A> a) {
a->print();
}
int main() {
std::shared_ptr<A> ptr_a(new A);
std::shared_ptr<B> ptr_b(new B);
ptr_a->print();
ptr_b->print();
ptr_b->A::print();
//THIS WORKS!
print(ptr_a);
print(ptr_b);
}
The last two "A" print correctly. The reason your function doesn't work is because Dog* != std::shared_ptr<Dog>.
std::shared_ptr<derived> is implicitly convertible to std::shared_ptr<base>, so you should have no errors in passing a derived pointer to a function that takes a base pointer unless you are using a bad compiler.
An example function that compiles fine:
class animal{
public:
virtual auto print() const -> void = 0;
};
class dog: public animal{
public:
auto print() const -> void{ std::cout << "I'm a Dog!\n"; }
};
class cat: public animal{
public:
auto print() const -> void{ std::cout << "I'm not a Dog!\n"; }
};
auto func(std::shared_ptr<animal> ptr){
ptr->print();
}
auto main() -> int{
auto dog_ptr = std::make_shared<dog>();
auto cat_ptr = std::make_shared<cat>();
func(dog_ptr);
func(cat_ptr);
}
Will print:
I'm a Dog!
I'm not a Dog!
Now I understand the problems that you are having. If you consistently use shared_ptr everywhere, you are able to pass shared_ptr objects of Base and Derived the same as raw pointers. I fleshed out the example that you gave.
The problem comes when you mix shared_ptr and raw pointers. The first problem goes away with overloading, where the shared_ptr function forwards to the function taking a raw pointer where the real work is done. The second problem goes away when calling with the underlying raw pointer obtained from the shared pointer.
class Animal
{
public:
};
class Dog : public Animal
{
public:
};
using std::shared_ptr;
typedef shared_ptr<Animal> animal_ptr;
typedef shared_ptr<Dog> dog_ptr;
class Buffer {
private:
std::deque<shared_ptr<Animal> > buffer_;
public:
void add(shared_ptr<Animal> req) {
buffer_.push_back(req);
}
shared_ptr<Animal> remove() {
shared_ptr<Animal> back = buffer_.back();
buffer_.pop_back();
}
};
void func1(Animal* ptr)
{}
void func1(shared_ptr<Animal> ptr)
{
func1(ptr.get());
}
void func2(Animal* ptr)
{}
int main()
{
Buffer buffer;
animal_ptr bPtr1(new Animal()); // buffer.add() works just fine
dog_ptr dPtr1(new Dog()); // requires upcasting before buffer.add()
animal_ptr dptr2(new Dog()); // returns error, as they are covariant
Dog* rawPtr = new Dog();
func2(rawPtr); // is ok.
func1(dPtr1); // is ok.
func1(rawPtr); // requires overloading func1 with shared_ptr and raw pointer signatures
func2(dPtr1.get()); // is okay when using underlying raw ptr
return 0;
}

C++11 - How to push this object into priority_queue with vector of shared_ptr?

I have a base class with a priority_queue like this:
class base
{
//...
std::priority_queue<std::shared_ptr<Obj>, std::vector<std::shared_ptr<Obj>>, obj_less> obj_queue;
//...
}
On my Obj class, I have a method that should push this object into the priority_queue:
void Obj::set ()
{
BaseServer& myObj = BaseFactory::getBase();
myObj.set(this); //<------ won't compile :(
}
And this set() will call a set() on my base class:
void base::set(const Obj& o)
{
obj_queue.push(o);
}
I want to use the this, to get the pointer to this same Obj, and push it into my vector, inside my priority_queue....
But it won't even compile, and I'm a bit lost...
Any ideas what I'm missing here?
You actually shouln't do this, since, it's really bad idea and you will have no problems only and only if you has raw pointer on Obj in place of calling set function. Idea of your code is strange, but, it's actually better to use shared_ptr and enable_shared_from_this.
class Obj : public std::enable_shared_from_this<Obj>
{
public:
// ...
void set()
{
BaseServer& myObj = BaseFactory::getBase();
myObj.set(std::shared_from_this()); //<------ won't compile :(
}
};
And BaseServer should have function set, that receives shared_ptr on Obj. And of course you should use shared_ptr<Obj> in code, that calls set. For example something like this
class Obj : public std::enable_shared_from_this<Obj>
{
private:
Obj() {}
public:
static std::shared_ptr<Obj> create()
{
return std::make_shared<Obj>();
}
// rest code
};
// code, that calls set function
auto object = Obj::create();
object->set();
myObj.set(this);
passes a pointer, but
void base::set(const Obj& o)
expects an object.
Do
void base::set(const Obj *o)
{
obj_queue.push(*o);
}
Or
myObj.set(*this);

Dynamically set vector class at runtime

Allow me to give some background. I have an abstract class, Foo.
class Foo {
public:
virtual void bar() = 0;
}
I have two classes that inherit from this class.
class FooOne : public Foo {
public:
void bar();
}
and
class FooTwo : public Foo {
public:
void bar();
}
Now in a completely different class, I want to create an array in a function that can hold instances of one of these two classes. The problem that I'm running into is that I cannot create an array with a dynamic type like this, can I? I'm used to Objective-C where I can create an object of type id.
Ideally, this is what I was looking for (pseudocode):
void someFunction(FooType type) {
class aClass = (type == FooTypeOne ? FooOne : FooTwo);
vector<aClass> container;
// Do something with the container.
}
Note: I cannot use C++11 in this project.
You could use smart pointer in STL container:
Foo* MakeFoo(FooType type)
{
switch(type)
{
case FooTypeOne :
return new FooOne();
break;
case FooTypeTwo :
return new FooTwo();
break;
default:
break;
}
return null;
}
void someFunction(FooType type)
{
std::vector<std::shared_ptr<Foo> > container;
std::shared_ptr<Foo> f_ptr(MakeFoo(type));
container.push_back(f_ptr);
// Do something with the container.
for(std::vector<std::shared_ptr<Foo> >::iterator iter = container.begin();
iter != container.end(); iter++)
{
(*iter)->bar(); // call derived object's bar function respectively
}
}
As you are using C++03, shared_ptr is available under std::tr1
Note:
You need to add virtual destructor to Foo
class Foo {
public:
virtual ~Foo() {}
virtual void bar() = 0;
};
Otherwise, you get undefined behavior if you delete an object of a derived type through a pointer to the base.
The only easy and clean option that I can think of is is templates. i.e if you want to avoid pointers as you say.
template <typename FooType>
void SomeFunction() {
vector<FooType> container;
// Do something with the container.
}
void SomeFunctionCaller(){
...
if(type == "FooOne")
SomeFunction<FooOne>();
else
SomeFunction<FooTwo>();
}
But it is quite different from your design and not sure if it will fit.
Edit: Ah if you are Ok with smart pointers then that is the way to go.