0

I have a class with the member

vector<shared_ptr<ParticleSystem>> particleSystems;

which has a method

void AddParticleSystem(shared_ptr<ParticleSystem> const sys)
{
    particleSystems.push_back(sys);
}

note the parameter. The method is called like so:

shared_ptr<ParticleSystem> psys = make_shared<ParticleSystem>(...);
scene.AddParticleSystem(psys);

It works, but why? Doesn't this get shared?


By default I don't try to pass around pointers. When considering function parameters, I either use const& when wish not to change anything on the passed variable or use & when plan to use methods that will change members of the given variable.

So By default I implemented the above method like so:

void AddParticleSystem(ParticleSystem const& sys)
{
    particleSystems.push_back(std::make_shared<ParticleSystem>(sys));
}

that I call like

shared_ptr<ParticleSystem> psys = make_shared<ParticleSystem>(...);
scene.AddParticleSystem(*psys);

This time it doesn't compile, stating

Error C2280 'Physics::ParticleSystem::ParticleSystem(const Physics::ParticleSystem &)': attempting to reference a deleted function

I traced back the issue using Output (I use VS) which has led me to

particleSystems.push_back(std::make_shared<ParticleSystem>(sys));

to the make_shared method, to be precise.

Now, This ParticleSystem extends Visual that has constructors and members like

Visual(string const &name, string const &path, const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr, bool gamma = false)
{
    this->name = string().append(name);
    model = make_unique<Model>(path, gamma);
    material = make_unique<Material>();
    shader = make_unique<Shader>(vertexPath, fragmentPath, geometryPath);
}

unique_ptr<Shader> shader;
unique_ptr<Material> material;
unique_ptr<Model> model;
virtual ~Visual() = default;

I get make_shared needs to copy stuff somehow. Is the problem that a ParticleSystem which is a Visual has unique_ptr members and by default make_shared knows not how to treat them?

Is this the reason the compiler deleted my default copy constructor? And if so, if I implement a copy constructor for all the classes that Visual has, including itself, I can pass that ParticleSystem const& as a parameter?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
agiro
  • 2,018
  • 2
  • 30
  • 62
  • @NathanOliver thanks for the edit. – agiro Oct 16 '18 at 20:35
  • Why do you want to copy the object? Whats the shared pointer doing if not sharing? – tkausl Oct 16 '18 at 20:35
  • I wish not to copy it, just share (have two classes with similar lifetime to share the ownership). Which parameter would copy? the `shared_ptr const` or the `ParticleSystem const&`? – agiro Oct 16 '18 at 20:36
  • Both would copy, but one copies the `shared_ptr`, the other copies the `ParticleSystem`. – tkausl Oct 16 '18 at 20:38
  • I guess the `shared_ptr const` copies the shared pointer itself (which points to the object). If I want to share **the object** and NOT copy it, does copying around the `shared_ptr` (the first, working solution) cut it? – agiro Oct 16 '18 at 20:40
  • 1
    Where is your [MCVE] please? – Paul Sanders Oct 16 '18 at 20:43
  • Take a look at [this](https://stackoverflow.com/questions/49776400/passing-a-shared-pointer-by-reference-or-by-value-as-parameter-to-a-class/49776497#49776497) – Mike Vine Oct 16 '18 at 20:43
  • @PaulSanders The question is kind of theoretical (as I don't fully understand how this passing around smart pointers work), not a debug-my-code one. To give a complete example I would have to pass _a lot of_ code. – agiro Oct 16 '18 at 20:48
  • @MikeVine so you say I should just pass a copy of the `shared_ptr` (it's fine by me :D )? Does the `std::move` require some copy constructors implemented? – agiro Oct 16 '18 at 20:50
  • If you wish to share it, why are you marking it as `const`? – Eljay Oct 16 '18 at 21:09
  • @Eljay valid claim, my bad, sorry. Went with the `shared_ptr const&` solution as stated in the answer. – agiro Oct 16 '18 at 21:31
  • 1
    A coworker of mine wrote up a good guide on shared_ptr, unique_ptr, raw pointer, reference usage: [GotW #91 Solution: Smart Pointer Parameters](https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/) – Eljay Oct 17 '18 at 20:59

1 Answers1

2

Each copy of a shared_ptr is a handle by which the shared object can be accessed. You can pass around references to the shared_ptr or copies of it and store copies of it in collections. Copies of shared_ptrs refer to the same underlying object. When the last shared_ptr that refers to the same underlying object is destroyed, the underlying object is destroyed.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • So when I pass it like `shared_ptr` to the function, before calling I have share count 1, then while passing I have 2, then during the `AddParticleSystem` method I have 3, then after that 2 (as the parameter ended) right? – agiro Oct 16 '18 at 20:45
  • 1
    @agiro That's correct. To avoid the extra increment/decrement, you could pass it by const reference (or move it into the collection). – David Schwartz Oct 16 '18 at 20:51
  • Thank you, will do that (though the program isn't multithreaded so I wouldn't have to worry that much about inc/dec, but one never knows). By the way, Why doesn't the `ParticleSystem&` work? – agiro Oct 16 '18 at 20:53
  • 2
    @agiro Because that tries to construct a new `ParticleSystem` as a copy of the one passed by reference, and `ParticleSystem` is uncopyable as its copy constructor is deleted. – David Schwartz Oct 16 '18 at 20:55
  • Two more question if I may - 1. It tried to copy because (from the reference) it has an address _of an object_ only and still has to forge the `shared_ptr` for the `vector`? 2. when I implement the copy constructor, I can pass my object around like that. _but I will get a new object and if I change that one, the original will not be affected?_ – agiro Oct 16 '18 at 20:59
  • 2
    @agiro think of your call `= std::make_shared(sys)` as the same thing as `= std::shared_ptr( new ParticleSystem(sys))` and you can see why the copy constructor is being called. These two calls are effectively the same thing. – samuelnj Oct 16 '18 at 21:03