4

I have been researching smart pointers and when they should be used vs raw pointers. My take away was that objects which OWN the pointer should instantiate that pointer as a smart pointer (unique_ptr/shared_ptr).

Then, when passing that pointer to various functions / classes that do not require ownership of it, pass a raw pointer (obtained from the smart pointer via .get()) with the caveat that the function / class that the raw pointer is sent to will NEVER outlive the scope of the function / class which owns it.

Is this understanding correct? It seems to make sense to me, but when I started to implement a specific routine my confidence dwindled.

The following code will better explain where my hangup is currently. I have a member vector of unique_ptrs to Mesh objects in Scene. I then pass the raw pointer of the created unique_ptr to instantiate an Actor object and finally move the unique_ptr into the scene->meshes vector.

Is the fact that an Actor has a Mesh* as a member not considered bad practice as long as the scope of any one Actor will never outlive that of the owning scene->meshes element?

#include <iostream>
#include <vector>
#include <memory>

class Mesh
{
public:
    int vec = 1;
    int ind = 2;
};

class Actor
{
private:
    Mesh * mesh;
public:
    Actor(Mesh* m) {
        this->mesh = m;
    }
};

class Scene
{
public:
    // meshes will always outlive all instantiated actors
    std::vector<std::unique_ptr<Mesh>> meshes;
    std::vector<Actor> actors;
};


int main()
{
    auto scene = std::make_unique<Scene>();

    auto m1 = std::make_unique<Mesh>();
    auto a1 = Actor(m1.get());
    scene->meshes.push_back(std::move(m1));

    auto m2 = std::make_unique<Mesh>();
    auto a2 = Actor(m2.get());
    scene->meshes.push_back(std::move(m2));

    std::cout << scene->meshes.size();

    std::cin.get();
    return 0;
}
whitwhoa
  • 2,389
  • 4
  • 30
  • 61
  • 2
    Raw *non owning* pointers are fine IMHO, as long as you *know* that the object they point to always lives longer than the raw pointer. – Jesper Juhl Feb 11 '20 at 17:35
  • To follow up @JesperJuhl point, entry from C++ Core Guidelines, R.3: A raw pointer (a T*) is non-owning: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ptr – R2RT Feb 11 '20 at 17:59

2 Answers2

3

Is the fact that an Actor has a Mesh* as a member not considered bad practice as long as ...

It is a code smell, sure. The class is easy to misuse. It has to be carefully documented that the constructor stores the pointer for the lifetime of the actor to alert the user to maintain the lifetime of the pointed mesh.

In case such as this, shared ownership would be safer. But it is not always the best choice if the performance overhead is significant.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

It's correct, but very ugly.

You could consider a third class implementing the relationship, and any actions acting on this relationship.

A Controller of sorts.

Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31