1
\$\begingroup\$

I would like to expose a nullable referance of a "mesh" object, so I am returning a const pointer. However, I explicitly want to prevent anyone handling this from deleting the object. The following is my solution.

Mesh class

class IMesh
{
public:
    virtual ~IMesh() = 0; // pure abstract
    
protected:
    IMesh() = default; // prevent creation outside of class
    
private:
    void operator delete(void * p){::operator delete(p);} // prevent deletion outside of class

    friend struct std::default_delete<IMesh>; // allow deletion by smart pointer
};


class Mesh : IMesh
{
public:
    static std::unique_ptr<IMesh> Create()
    {
        std::unique_ptr<IMesh> px {new Mesh{}};
        return px;
    }

private:
    Mesh() = default; // prevent creation outside of class
};

MeshLoader class can get intances of the Meshes

class MeshLoader
{
public:
    explicit MeshLoader(gef::Platform& platform_);
    Result LoadMeshScene(string const & filepath);
    IMesh const * GetMesh(string const& name);
private:
    gef::Platform& platform_;
    gef::Scene model_scene_;
    
    std::map<string, std::unique_ptr<IMesh>> meshes;
    
};

GetMesh() function

IMesh const * AnimationSystem::MeshLoader::GetMesh(string const & name)
{
    const auto mesh = meshes.find(name);
    
    if(mesh == meshes.end())
        return nullptr;
    
    return mesh->second.get();
}

Consumers can then call:

auto mesh = meshLoader.GetMesh("ID");

And use this mesh as they like.

However, attempring to call:

delete mesh;

Will produce the following compliation error

[C2248] 'IMesh::operator delete': cannot access private member declared in class 'IMesh'

Is this a good solution? Are there any dangers I'm missing? Is ::operator delete(p); going to delete my object as expected?

\$\endgroup\$
1
  • \$\begingroup\$ Your code doesn't compile with GCC or Clang, whether or not you call delete mesh: error: deleted function '~Mesh' cannot override a non-deleted function. \$\endgroup\$
    – G. Sliepen
    Oct 30, 2022 at 20:41

1 Answer 1

3
\$\begingroup\$

It might be simpler to make your class a wrapper around the smart pointer, internally. (Although then you would need to re-invent the shared_ptr and weak_ptr if you need them.) The simplest implementation might be, using IMesh = std::unique_ptr<Mesh>;.

Because the destructor is private: and not protected:, subclasses cannot be deleted. If you fix this by letting all derived classes use the destructor, however, there is now an easy way to work around the restrictions, by deriving a subclass that adds a public: constructor and destructor. You might consider this an advantage or a disadvantage.

Another way this implementation is more restrictive than it should be, if what you want is to allow smart pointers and only smart pointers, is that this blocks STL smart pointers with custom deleters.

I suppose I would frame-challenge this. The language is not really designed to enforce the use of this type only through a smart pointer. There’s no particular advantage to doing so: you’ve already blocked people from creating a new Mesh on the heap, or a Mesh on the stack. Forcing programmers to create a std::vector<std::shared_ptr<IMesh>> instead of a std::vector<Mesh> whose memory is just as safely-managed as a smart pointer is pessimal. You’re blocking other legitimate patterns as well, such as smart pointers to structures containing a mesh.

It’s probably possible to work around these restrictions if the programmer needs to, but consider whether it would be better just to use smart pointers exclusively in your own API, and not try to have the compiler enforce this on every use of the class in client code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.