-2

I have a scenario in which I need to collect all the objects of a type in a collection, but I also need a collection of some of its inherited types. Example:

class Particle: public someClass
{
    ...
    public:
        static std::vector<std::shared_ptr<Particle>> particleCollection;
}

class ChargedParticle: public Particle
{
    ...
    public:
    static std::vector<std::shared_ptr<ChargedParticle>> chargedParticleCollection;
}

However when I want to destroy these objects, I actually call the destructor twice for every ChargedPartice:

Particle::particleCollection.clear(); // Okay
ChargedParticle::chargedParticleCollection.clear(); // Error: particles are already deleted

How can I both have a collection of the child objects stored in its static container and have smart pointers pointing on them by one of their parent classes?

I want to be able to create objects from the parent class too, and have the parent's static smart pointer vector be the owner of these objects.

My idea is that I somehow define a custom deleter for the parent class smart pointers that only calls the destructor, when the object is not an element of the child class' collection. Is this possible?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adam Hunyadi
  • 1,890
  • 16
  • 32
  • Why do you use static member to store this particles? – Alex Aparin Nov 05 '16 at 11:06
  • When using `shared_ptr` properly, double destructor calls should not happen. You should show more code, possibly a mcve: http://stackoverflow.com/help/mcve – Waldheinz Nov 05 '16 at 11:07
  • @АлександрЛысенко Simple use-case: I want to calculate the force on a charged particle, due to electrostatic interactions. For this I need to know all the other charged particle positions. – Adam Hunyadi Nov 05 '16 at 11:09
  • Maybe then you should keep particles only in base class? Without creating possible duplicates? – Alex Aparin Nov 05 '16 at 11:13
  • I'm not creating duplicates, just having two owners of the same object. And I need to have separate collections for both of them, because they are used for diferent kinds of operations, and I don't want to filter a collection and convert it to a collection of different kind of pointers (which is actually quite expensive) every time I call a child operation. – Adam Hunyadi Nov 05 '16 at 11:15
  • This is a case where you need to provide an MCVE if anyone is to have a hope of helping you. Lacking an MCVE, I suggest you need to look closely at the documentation of `shared_ptr`. A `shared_pointer` can actually manage two distinct pointers - one it owns and one it shares. If you mess up handling of that sort of thing, consequences can be the sort of problem you're having. – Peter Nov 05 '16 at 11:21
  • You are probably creating shared pointers from regular pointers. This defeats the purpose of shared pointers. Remove regular pointers to `Particle` and all its derived classes from your program and replace them with shared pointers. One kind of pointer you cannot remove is `this`. If you have to create a shared pointer from `this`, you must inherit from `std::enable_shared_from_this`. – n. m. could be an AI Nov 05 '16 at 11:49
  • @n.m. He wants to create a self-registering class, which can't be done with `std::shared_ptr` and `std::enable_shared_from_this`, see his comment in my answer and [here](http://coliru.stacked-crooked.com/a/d82244848f9ff1ca) – Danh Nov 05 '16 at 11:53
  • 1
    From cppreference, _It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17)._ – Danh Nov 05 '16 at 12:01
  • @n.m. I only create smart pointers from "this". std..enable_shared_from_this does not seem to help this cause... – Adam Hunyadi Nov 05 '16 at 12:08
  • The real problem is you can't manage the non-existence object – Danh Nov 05 '16 at 13:00
  • Yes, you cannot generally use `shared_from_this` in a constructor before any other shared ptr takes ownership. If you need this, you can create the very first shared pointer from `this` without using `shared_from_this`, but you need to be careful with it (ie.g. `make_shared()` probably won't work with this scheme). Better write a factory and register from the creation method rather than from a constructor. – n. m. could be an AI Nov 07 '16 at 18:39

2 Answers2

1

Every ChargedParticle is at the same time is a Particle so calling Particle::particleCollection.clear(); would be enough to remove all allocated objects.

To use shared pointer in your case you need to have base class (either someClass or Particle) to be inherited from std:: enable_shared_from_this thus shared pointer created from it will share the same counter. In your example these are two different instances of shared pointer that know nothing about each other.

And I don't see any reason for virtual destructor to not be enough for your needs.

Teivaz
  • 5,462
  • 4
  • 37
  • 75
  • The clearing of the particleCollection does not help me with the chargedParticleCollection. After the one-sided clear if I loop on it, it will contain dangling pointers. I use virtual destructors, but I've never heard of this std::enable_shared_from_this class. I will check it out, thanks :) – Adam Hunyadi Nov 05 '16 at 11:18
  • @AdamHunyadi You can consider using `std::weak_ptr`, or removing items from appropriate array in destructor. – Teivaz Nov 05 '16 at 11:21
  • How do I use std::weak_ptr in this case? – Adam Hunyadi Nov 05 '16 at 11:41
  • @AdamHunyadi weak pointer has member `expired` which can tell if the object is already deleted. But it all depends on how you want to manage the ownership – Teivaz Nov 05 '16 at 11:46
  • I understand, but how do I put weak pointers of the derived class to a vector? I cannot make them weak ptrs of base class, because I'm using them in a collection of derived objects. I cannot make it a weak ptr of derived either, because then I cannot make it stick to a shared ptr of a base class. – Adam Hunyadi Nov 05 '16 at 12:01
  • @AdamHunyadi weak pointers can be converted to shared pointers easily. But I don't know who owns these objects. If owning is shared than use shared pointers everywhere, if lifetime of the object defined in single place than use shared pointer there and weak pointers in all other places. – Teivaz Nov 05 '16 at 12:10
  • He wants to use it from constructor to create self -registering class, then it's impossible because there are no owner at the first place – Danh Nov 05 '16 at 12:57
  • @teivaz anyway, it only works if and only if all constructor doesn't throw, which is hard to archive – Danh Nov 05 '16 at 13:22
0

If you use it properly, there's no problem, class's name is not the same with your class name, but I don't think it matters:

#include <iostream>
#include <type_traits>
#include <tuple>
#include <vector>
#include <memory>
struct Base
{
    Base() { std::cout << "  Base::Base()\n"; }
    // Note: non-virtual destructor is OK here
    ~Base() { std::cout << "  Base::~Base()\n"; }
};

struct Derived: public Base
{
    Derived() { std::cout << "  Derived::Derived()\n"; }
    ~Derived() { std::cout << "  Derived::~Derived()\n"; }
};

int main() {  
    std::vector<std::shared_ptr<Base>> base_vector;
    std::vector<std::shared_ptr<Derived>> derived_vector;
    auto d = std::make_shared<Derived>();
    derived_vector.push_back(d);
    base_vector.push_back(d);
    // 2 function call below does not matter
    base_vector.clear();
    derived_vector.clear();

}

Demo is expanded from example in Cpp Reference

You can't create a self-registering class with std::shared_ptr because you need to inherit from std::enable_shared_from_this, but you can't call shared_from_this() in the constructor because:

It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).

Danh
  • 5,916
  • 7
  • 30
  • 45
  • This is no good, since in my Base class constructor call, I cannot have a pointer to a derived class. – Adam Hunyadi Nov 05 '16 at 11:31
  • @AdamHunyadi that means you want to self-register your class to that list? – Danh Nov 05 '16 at 11:33
  • @AdamHunyadi And the problem is, when the constructor hadn't finished, the object haven't existed, if the constructor throws, the object isn't existed, that says your vector have a non-exist object – Danh Nov 05 '16 at 11:36
  • Hence, consider using Factory design pattern instead – Danh Nov 05 '16 at 11:38
  • Yes, self registering as in giving ownership to these containers during construction. – Adam Hunyadi Nov 05 '16 at 11:40
  • Then, what happens if the construction fail after registration, let's say it's success in base classs and fail in derived class – Danh Nov 05 '16 at 11:41
  • 1
    @AdamHunyadi To be more clear, `std::enable_shared_from_this` complains about `bad weak_ptr` as you can see [here](http://coliru.stacked-crooked.com/a/8f609864c32abd83) and [here](http://coliru.stacked-crooked.com/a/d82244848f9ff1ca) – Danh Nov 05 '16 at 11:51