как заблокировать использование std :: make_shared <T> - PullRequest
3 голосов
/ 19 сентября 2019

Я знаю, что могу предотвратить обычное выделение кучи пользовательского класса и его потомков, сделав класс operator new закрытым, но есть ли способ запретить пользователю библиотеки вызывать std::make_shared для пользовательского класса (или егопотомки)?По-видимому, простое создание operator new private в классе не останавливает его.

Обратите внимание, что я не хочу полностью запрещать создание общих указателей каким-либо образом, поскольку я намерен все еще иметь возможность производитьstd::shared_ptr для моего пользовательского класса путем вызова метода статического генератора, но я все еще хочу, чтобы другие не пытались напрямую вызывать std::make_shared в моем классе.

РЕДАКТИРОВАТЬ:

Для адресацииЗапросы ниже, моя конечная цель здесь - гибкий и многократно используемый механизм для извлечения shared_ptr из необработанного указателя.std::enable_shared_from_this, к сожалению, не очень дружелюбен, когда речь идет о наследовании и особенно множественном наследовании.

Кроме того, поскольку я намерен использовать это, как в классе шаблона, использующем CRTP , для дочернего класса T может быть несколько сложнее явно сделать шаблонного родителякласс, который он наследует от друга, так что он может получить доступ к закрытым конструкторам.

Ответы [ 3 ]

6 голосов
/ 19 сентября 2019

Это не характерно для std::shared_ptr, но вы можете сделать конструктор частным, и таким образом заставить все экземпляры класса генерироваться из вашего статического метода.

#include <iostream>
#include <memory>

class Foo {
    private:
    Foo() = default;

    public:
    static std::shared_ptr<Foo> make() {
        return std::shared_ptr<Foo>(new Foo);
    }
};

int main() {
    //Foo f1;
    //auto f2 = std::make_shared<Foo>(); 
    //above does not work since the constructor is private
    auto h = Foo::make();
}

Вы также можете использовать дедупликаторыпредложить и использовать закрытый ключ, чтобы конструктор был недоступен за пределами класса.

#include <iostream>
#include <memory>

class Foo {
    private:
    struct FooKey {};

    public:
    Foo(FooKey) {};

    static std::shared_ptr<Foo> make() {
        return std::make_shared<Foo>(FooKey{});
    }
};

int main() {
    //Foo f1{Foo::FooKey{}};
    //auto f2 = std::make_shared<Foo>(Foo::FooKey{}); 
    //above does not work since Foo::FooKey is private
    auto h = Foo::make();
}
2 голосов
/ 19 сентября 2019

Блокирование выделения пользовательской кучи типа T - это на самом деле не то, что вы можете сделать в C ++.По крайней мере, не так долго, как они могут создавать T с.Даже если вам удастся запретить make_shared<T>, ваш пользователь все равно может сделать это:

unique_ptr opt = new optional<T>;
opt->emplace(...);

T внутри *opt определенно находится в куче.Любое количество других подобных гимнастики может достичь того же эффекта.Вы даже можете вызвать make_shared<optional<T>> с параметрами in_place.

Пока T имеет общедоступные конструкторы или существует какой-либо общедоступный способ построения T, который возвращает значениеТаким образом, пользователи могут найти способы хранения этого T в куче.

Так что единственный способ предотвратить это - сделать все ваши конструкторы закрытыми (будь то напрямую с помощью ключа private или private).введите или любой другой механизм, который вам нужен) и предоставьте только общедоступные функции, которые возвращают shared_ptr<T> s.

За пределами макросов, нет механизма C ++, который просто заставляет тип работать таким образом.Это должно быть сделано индивидуально для каждого типа, который вы хотите работать таким образом.

0 голосов
/ 20 сентября 2019

Приведенные выше ответы являются лучшим решением проблемы.Но если вы пытаетесь не позволить make_shared создать новый экземпляр вашего класса, возможно, это полезно.

#include <iostream>
#include <memory>
using namespace std;

class A {
private:
    void * operator new(size_t size) {}
};

template<> shared_ptr<A> make_shared() {
    cout << "this was called.\n";
    return nullptr;
}

int main() {
    A a;
    shared_ptr<A> p = make_shared<A>(a);
    shared_ptr<A> q = make_shared<A>();
    cout << "p: " << p << endl;
    cout << "q: " << q << endl;

    return 0;
}

'p' получает адрес объекта, а 'q' - нет.

Примечание: в компиляторе требуется опция -fpermissive.

...