Страницы

Поиск по вопросам

Показаны сообщения с ярлыком smart-pointer. Показать все сообщения
Показаны сообщения с ярлыком smart-pointer. Показать все сообщения

среда, 4 марта 2020 г.

PIMPL на unique или shared указателях?

#cpp #перегрузка_операторов #smart_pointer


Привожу код заготовки реализации идиомы pImpl с этого сайта.

// in header file
class widget 
{
public:
    widget();
    ~widget();
private:
    class impl;
    unique_ptr pimpl;
};

// in implementation file
class widget::impl 
{
    // :::
};

widget::widget() : pimpl{ new impl{ /*...*/ } } { }
widget::~widget() { }


Далее там написано следующее:


  Prefer to hold the Pimpl using a unique_ptr. It’s more efficient than
  using a shared_ptr, and correctly expresses the intent that the Pimpl
  object should not be shared.


Вопрос: в чем конкретно эффективность (предпочтительное более быстрое перемещение?)
использования std::unique_ptr и почему предлагается не разделять реализацию между несколькими
экземплярами? Я ранее задавал вопрос относительно идиомы copy-on-write, почему бы не
использовать реализацию идиомы pImpl на указателе типа shared_ptr и дополнить ее вот
такими реализациями операторов, характерными для COW идиомы:

    // Non-const * and -> , copying
    T& operator*()
    {
        copy();
        return *m_sp;
    }

    T* operator->()
    {
        copy();
        return m_sp.operator->();
    }

    // Const * and -> methods no need to copy
    const T& operator*() const
    {
        return *m_sp;
    }

    const T* operator->() const
    {
        return m_sp.operator->();
    }

    


Ответы

Ответ 1



Для начала нужно понимать разницу в идеологии между shared_ptr и unique_ptr. Первый подразумевает, что объект будет в общем владении, а unique_ptr только единоличное владение. Так как pimpl подразумевает единоличное владение (не, можно конечно сделать и расшаренное, но это как то не нормально), то unique_ptr в самый раз. Почему же оно быстрее? все очень просто. shared_ptr внутри себя содержит как минимум счетчик ссылок либо на атомиках, либо с мютексами. В любом случае это не быстро. unique_ptr это все не нужно. В многих случаях компилятор может оптимизировать до такой степени, что будет не хуже голого указателя. почему предлагается не разделять реализацию между несколькими экземплярами потому что это будет уже не pimpl. дополнить ее вот такими реализациями операторов, характерными для COW идиомы: Ваша реализация требует наличие некой функции copy. И второе - она не очень thread-safe. Почему такое не сделали в shared_ptr? Потому что это был бы cow_ptr. Это лишняя функциональность, которая обычно не нужна (если бы она была нужна, то наверно она появилась бы и в бусте, откуда пришел этот умный указатель), а в с++ не принято "платить за то, что не используется".

Ответ 2



Если вы дополните умный указатель этими операторами - это будет уже не shared_ptr, а уже какой-нибудь cow_ptr. И да, так тоже будет работать. Тут главное - найти способ вынести copy() в модуль реализации, потому что нельзя скопировать необъявленный тип данных. Как-то так в итоге должно получиться: class widget { public: widget(); ~widget(); private: class impl; impl* copy(pimpl* impl); cow_ptr pimpl; }; Однако, на самом деле операция копирования объекта - очень странная по смыслу. И в большинстве прикладных случаев ее явно запрещают. А если копирование запрещено архитектурно, то зачем все эти сложности с реализацией?

среда, 1 января 2020 г.

Не могу добавить в вектор умный указатель

#cpp #vector #stl #smart_pointer


Имеется класс MyClass, разумеется с конструктором, нужно создать вектор умных указателей
на объекты этого класса. Сам указатель создается, но при попытке добавления в вектор
вылезает ошибка . Что я упустил?

#include 
#include  

using namespace std;
Int main()
{
    vector> vectorPtr;
    unique_ptr p1(new MyClass);
    // до этого момента всё в порядке
    vectorPtr.push_back(p1);
    return 0;
}

    


Ответы

Ответ 1



std::unique_ptr не имеет конструктора копирования, поэтому, чтобы поместить его в вектор, его нужно переместить туда: vectorPtr.push_back(std::move(p1)); Или так: vectorPtr.push_back(std::make_unique()) Либо же создавать прямо в векторе: vectorPtr.emplace_back(new MyClass);

суббота, 15 июня 2019 г.

Копирование из shared_ptr в unique_ptr

Добрый вечер. Сразу к теме: есть контейнер map> mymap; (MySet - пользовательский тип). Есть другой контейнер: forward_list> myset; Вопрос: как из forward_list копировать элементы в map (из shared_ptr в unique_ptr)?


Ответ

Как я понимаю, объект класса std::map> должен быть владельцем указателей на объекты MySet, а потому при копировании нужно создавать копии объектов, хранящихся в std::forward_list>
Для этих целей можно использовать либо обычный цикл, либо стандартные алгоритмы, как, например, std::transform
Ниже приведена демонстрационная программа, которая показывает, как можно использовать алгоритм std::transform для копирования элементов из одного контейнера в другой. Для наглядности в вызовы конструкторов и деструкторов класса, используемого в качестве базового элемента, я включил вывод на консоль.
#include #include #include #include #include #include #include
struct A { A() { std::cout << "A::A()" << std::endl; } A( const A & ) { std::cout << "A::A( const A & )" << std::endl; } ~A() { std::cout << "A::~A" << std::endl; } A & operator =( const A & ) { std::cout << "A::operator =( const A & )" << std::endl; return *this; } };
int main() { std::forward_list> lst; lst.push_front( std::shared_ptr( new A ) );
std::map> m;
std::transform( lst.begin(), lst.end(), std::inserter( m, m.end() ), []( auto p ) { return std::make_pair( 1, std::unique_ptr
( new A( *p.get() ) ) ); } );
return 0; }
Вывод на консоль будет:
A::A() A::A( const A & ) A::~A A::~A

вторник, 21 мая 2019 г.

PIMPL на unique или shared указателях?

Привожу код заготовки реализации идиомы pImpl с этого сайта
// in header file class widget { public: widget(); ~widget(); private: class impl; unique_ptr pimpl; };
// in implementation file class widget::impl { // ::: };
widget::widget() : pimpl{ new impl{ /*...*/ } } { } widget::~widget() { }
Далее там написано следующее:
Prefer to hold the Pimpl using a unique_ptr. It’s more efficient than using a shared_ptr, and correctly expresses the intent that the Pimpl object should not be shared.
Вопрос: в чем конкретно эффективность (предпочтительное более быстрое перемещение?) использования std::unique_ptr и почему предлагается не разделять реализацию между несколькими экземплярами? Я ранее задавал вопрос относительно идиомы copy-on-write, почему бы не использовать реализацию идиомы pImpl на указателе типа shared_ptr и дополнить ее вот такими реализациями операторов, характерными для COW идиомы:
// Non-const * and -> , copying T& operator*() { copy(); return *m_sp; }
T* operator->() { copy(); return m_sp.operator->(); }
// Const * and -> methods no need to copy const T& operator*() const { return *m_sp; }
const T* operator->() const { return m_sp.operator->(); }


Ответ

Для начала нужно понимать разницу в идеологии между shared_ptr и unique_ptr. Первый подразумевает, что объект будет в общем владении, а unique_ptr только единоличное владение. Так как pimpl подразумевает единоличное владение (не, можно конечно сделать и расшаренное, но это как то не нормально), то unique_ptr в самый раз.
Почему же оно быстрее? все очень просто. shared_ptr внутри себя содержит как минимум счетчик ссылок либо на атомиках, либо с мютексами. В любом случае это не быстро. unique_ptr это все не нужно. В многих случаях компилятор может оптимизировать до такой степени, что будет не хуже голого указателя.
почему предлагается не разделять реализацию между несколькими экземплярами
потому что это будет уже не pimpl.
дополнить ее вот такими реализациями операторов, характерными для COW идиомы:
Ваша реализация требует наличие некой функции copy. И второе - она не очень thread-safe.
Почему такое не сделали в shared_ptr? Потому что это был бы cow_ptr. Это лишняя функциональность, которая обычно не нужна (если бы она была нужна, то наверно она появилась бы и в бусте, откуда пришел этот умный указатель), а в с++ не принято "платить за то, что не используется".