enable_shared_from_this и объекты в стеке - PullRequest
10 голосов
/ 26 августа 2010

Как насчет вызова shared_from_this для объектов, размещенных в стеке?enable_shared_from_this в списке базовых классов является индикатором для пользователя производного класса для создания его только в куче (и мы будем надеяться на правильное использование класса), или мы можем иметь более надежную защиту от таких ошибок?Или я не понимаю некоторые моменты?Пример кода: <pre> class C : public enable_shared_from_this<C> { public: shared_ptr<C> method() { shared_from_this(); } };</p> <p>void func() { C c; shared_ptr<C> ptr = c.method(); // exception comming from shared_from_this() }

Ответы [ 2 ]

12 голосов
/ 26 августа 2010

Таким образом, чтобы защититься от этой проблемы, вы можете сделать ваши конструкторы частными и предоставлять только функции создания, которые возвращают shared_ptr - таким образом объект не может быть размещен в стеке, например:

class C : public enable_shared_from_this<C>
{
public:
  static shared_ptr<C> create() { return shared_ptr<C>(new C() ); }
  shared_ptr<C> method() { shared_from_this(); }

private:
  C() {...}

  // Make operator= and C(const C&) private unimplemented
  // so the used cant do bad things like C c( * c_ptr );
  C& operator=( const C & );
  C( const C & );
};


void func()
{
  C c; // This doesnt compile
  shared_ptr<C> ptr = c.method(); // So you can never get this
}

void altfunc()
{
  shared_ptr<C> c_ptr = C::create();
  C & c_ref = *c;
  shared_ptr<C>  ptr = c_ref.method(); // OK
}

Если вы захотите использовать anoperator =, вы можете предоставить функцию клонирования, используя частный реализованный конструктор копирования, что-то вроде этого

// This goes in class C
shared_ptr<C> C::clone() const
{
  return shared_ptr<C>( new C(*this) );
}

// This is how you can use it
shared_ptr<C> c2 = c1->clone();
2 голосов
/ 27 апреля 2018

Я нашел решение.

В библиотеке Dune используется совместимая со стеком enable_shared_from_this адаптация шаблона класса.

Проверьте исходный код здесь или проверьте мой пример с помощью быстрого копирования, вставки и очистки кода:

#include <cstdio>
#include <cassert>
#include <memory>
#include <iostream>

template<class T>
struct null_deleter
{
    void operator() (T*) const {}
};
template<typename T>
inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t)
{
    return std::shared_ptr<T>(&t, null_deleter<T>());
}

template<typename T, typename T2>
inline std::shared_ptr<T2> stackobject_to_shared_ptr(T & t)
{
    return std::shared_ptr<T2>(dynamic_cast<T2*>(&t), null_deleter<T2>());
}

template<typename T>
class stack_compatible_enable_shared_from_this
: public std::enable_shared_from_this<T>
{
public:
    std::shared_ptr<T> shared_from_this()
    {
        try
        {
            return std::enable_shared_from_this<T>::shared_from_this();
        }
        catch (std::bad_weak_ptr&)
        {
            _local_ptr = stackobject_to_shared_ptr(*static_cast<T*>(this));
            return _local_ptr;
        }
    }

    std::shared_ptr<const T> shared_from_this() const
    {
        try
        {
            return std::enable_shared_from_this<T>::shared_from_this();
        }
        catch (std::bad_weak_ptr&)
        {
            _local_ptr = stackobject_to_shared_ptr(*const_cast<T*>(static_cast<const T*>(this)));
            return _local_ptr;
        }
    }

private:

    mutable std::shared_ptr<T> _local_ptr;
};

struct MyObj : public stack_compatible_enable_shared_from_this<MyObj>{};

int main (int argc, char **argv) {
    //std::shared_ptr<MyObj> so = std::make_shared<MyObj>(6);
    MyObj o{};
    auto * so = &o;
    {
        auto l = std::weak_ptr<MyObj>(so->shared_from_this());
        auto shared = l.lock();
        if (shared) { } //use it
    }
}
...