Samuel
Samuel

Reputation: 6490

Is there a way to prevent assignment of pointers?

A tricky question. If would like to write a function that returns a pointer to some IFoo object, is it possible to prevent the assignment of this pointer? I do not want to make IFoo a singleton and I can hide or delete the copy and assignment operators, but does C++ actually allow a pattern, where I explicitly have to call somebody else to obtain an object?

The background question is: I am thinking about some sort of dependency container, where you should always ask the container to get some IFoo * (pointer for the sake of polymorphism). The user should never be able to save it to some local variable or member to avoid a stalled reference to it. (For scenarios where the container is instructed to return no longer Foo, which is derived from IFoo but Bar)

Edit for clarification, although user R Sahu already said that is not possible. Indeed the example of Mark B was a perfect illustration of what I wanted to prevent:

IFoo* foo_ptr_I_will_keep_forever = obj->getIFoo();

When I wouldn't have interfaces but only explicit instance of types I could return a reference, which given a private operator= and copy ctor would suffice.

Upvotes: 3

Views: 687

Answers (3)

R Sahu
R Sahu

Reputation: 206567

Your title says:

Is there a way to prevent assignment of pointers?

No, you can't prevent that if your function returns a pointer.

However, if you return a handle, which can be a pointer to a type that is only forward declared or an integral value that can be used to come up the real object, and make sure that all the real functionality works with the handle, then you can have more freedom over when you can delete the real object without the risk of leaving the client code with a dangling pointer.

Here's a simple program that demonstrates the concept.

#include <iostream>
#include <set>

// Foo.h

struct Foo;
using FooHandle = Foo*;

FooHandle createFoo();

void fooFunction1(FooHandle h);

void fooFunction2(FooHandle h);

// Test Program

int main()
{
   FooHandle h = createFoo();
   fooFunction1(h);
   fooFunction2(h);
   fooFunction1(h);
   return 0;
}

// Foo implementation.

namespace FooImpl
{
   std::set<Foo*>& getLiveFooObjects()
   {
      static std::set<Foo*> liveObjects;
      return liveObjects;
   }

   bool isValid(Foo* h)
   {
      return (getLiveFooObjects().find(h) != getLiveFooObjects().end());
   }
}

using namespace FooImpl;

struct Foo {};

FooHandle createFoo()
{
   FooHandle h = new Foo{};
   getLiveFooObjects().insert(h);
   return h;
}

void fooFunction1(FooHandle h)
{
   if ( isValid(h) )
   {
      std::cout << "In fooFunction1.\n";
   }
   else
   {
      std::cout << "Ooops. The handle is no longer valid.\n";
   }
}

void fooFunction2(FooHandle h)
{
   std::cout << "In fooFunction2.\n";
   delete h;
   getLiveFooObjects().erase(h);
}

Output:

In fooFunction1.
In fooFunction2.
Ooops. The handle is no longer valid.

Upvotes: 2

cdhowie
cdhowie

Reputation: 168988

A middle ground answer that would prevent accidentally storing a pointer to a particular implementation, but wouldn't prevent someone from doing it on purpose:

template <typename T> class service_wrapper;

class service_manager
{
    template <typename T> friend class service_wrapper;

public:
    template <typename T>
    service_wrapper<T> get() const;

private:
    template <typename T>
    T* get_instance() const;
};

template <typename T>
class service_wrapper
{
    friend class service_manager;

public:
    T* operator->() const;

private:
    service_wrapper(service_manager const & p_sm) : sm(p_sm) { }
    service_manager const & sm;
};

template <typename T>
T* service_wrapper<T>::operator->() const
{
    return sm.get_instance<T>();
}

Your manager only dispenses instances of service_wrapper<T>. The operator-> implementation allows invoking on the service using wrapper->method(...);, and always fetches the implementation pointer from the service manager.

This can be circumvented like:

T *ptr = sm.get<T>().operator->();

But that's not something you can accidentally do.

Upvotes: 1

Lou Franco
Lou Franco

Reputation: 89152

Give them back an object (that they can store if they want) that always looks up the real one via private (friend) interfaces.

For example, an IFooCaller that implements IFoo by getting the current IFoo and forwarding all calls to it.

Upvotes: 2

Related Questions