How to force only smart pointers instance for a class?
Asked Answered
D

2

13

I've been working on a way to prevent user of using a class without smart pointers. Thus, forcing them to have the object being heap allocated and managed by smart pointers. In order to get such a result, I've tried the following :

#include <memory>
class A
{
private :
    ~A {}
    // To force use of A only with std::unique_ptr
    friend std::default_delete<A>;
};

This work pretty well if you only want your class users being capable of manipulating instance of your class through std::unique_ptr. But it doesn't work for std::shared_ptr. So I'd like to know if you had any ideas to get such a behavior. The only solution that I've found is doing the following (using friend std::allocator_traits<A>; was unsufficient) :

#include <memory>
class A
{
private :
    ~A {}
    // For std::shared_ptr use with g++
    friend __gnu_cxx::new_allocator<A>;
};

But this solution is not portable. Maybe I'm doing it the wrong way.

Drynurse answered 16/6, 2013 at 15:38 Comment(1)
Nice solution...okay, this wont work for STL but I always wanted this for my own SharedPtr implementation and this solves my problem!Ulmaceous
M
19

Create a friend'd factory function that returns a std::unique_ptr<A>, and make your class have no accessible constructors. But make the destructor available:

#include <memory>

class A;

template <class ...Args>
std::unique_ptr<A> make_A(Args&& ...args);

class A
{
public:
    ~A() = default;
private :
    A() = default;
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    template <class ...Args>
    friend std::unique_ptr<A> make_A(Args&& ...args)
    {
        return std::unique_ptr<A>(new A(std::forward<Args>(args)...));
    }
};

Now your clients can obviously get a unique_ptr<A>:

std::unique_ptr<A> p1 = make_A();

But your clients can just as easily get a shared_ptr<A>:

std::shared_ptr<A> p2 = make_A();

Because std::shared_ptr can be constructed from a std::unique_ptr. And if you have any user-written smart pointers, all they have to do to be interoperable with your system is create a constructor that takes a std::unique_ptr, just like std::shared_ptr has, and this is very easy to do:

template <class T>
class my_smart_ptr
{
    T* ptr_;
public:
    my_smart_ptr(std::unique_ptr<T> p)
        : ptr_(p.release())
    {
    }
    // ...
};
Mean answered 16/6, 2013 at 16:48 Comment(5)
Nice answer ! Thanks, that's a lot cleaner than my solution.Drynurse
When there are more classes B, C etc. with a common interface that need such a make_B(), make_C() factory functions, I tend to prefer static public member templates T::make(). This makes it a bit easier to set up a full-blown factory + registry class, where the concrete types make() functions can be registered from a type-list at compile-time.Arnst
@TemplateRex: Agreed. That's a nice naming convention for factory functions.Mean
If I am using C++14, std::make_unique function template should be preferred, right? Should I use it in a factory function, or specialize it for class A?Ambroseambrosi
@ElsaTse: If all of your constructors are private, you will need to friend std::make_unique, which will probably make its use more trouble than just reimplementing it as shown in my answer.Mean
H
0

As there is no general term "smart pointer" what you want is not possible.

What you can do, is supporting some known set of smart pointers. The usual solution starts as yours, making ctor or dtor private, and adds factory functions. That can return the instance packed with your desired smart pointers. If you just want to support unique_ptr and shared_ptr, that meaans two factory functions, hardly too much. (note that those pointers allow smuggling out the raw pointer through simple interface so the control is not full.)

Holierthanthou answered 16/6, 2013 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.