在构造函数中使用shared_from_this()

时间:2016-11-19 00:57:28

标签: c++ memory shared-ptr

如您所知,由于包含该类的shared_pointer尚未存在,因此无法使用对象构造函数中的std :: enable_shared_from_this和shared_from_this()对。但是,我真的很想要这个功能。我尝试过自己的系统,似乎工作正常。

namespace kp
{    

template <class T>
void construct_deleter(T *t)
{
  if(!t->_construct_pself)
  {
    t->~T();
  }

  free(t);
}

template <class T, typename... Params>
std::shared_ptr<T> make_shared(Params&&... args)
{
  std::shared_ptr<T> rtn;
  T *t = (T *)calloc(1, sizeof(T));
  t->_construct_pself = &rtn;
  rtn.reset(t, construct_deleter<T>);
  t = new(t) T(std::forward<Params>(args)...);
  t->_construct_pself = NULL;
  t->_construct_self = rtn;

  return rtn;
}

template <class T>
class enable_shared_from_this
{
public:
  std::shared_ptr<T> *_construct_pself;
  std::weak_ptr<T> _construct_self;

  std::shared_ptr<T> shared_from_this()
  {
    if(_construct_pself)
    {
      return *_construct_pself;
    }
    else
    {
      return _construct_self.lock();
    }
  }
};

}

有人能发现这种逻辑中的任何缺陷吗?在构造函数调用之前,我基本上使用placement new来指定类中的shared_ptr指针。

目前我可以这样使用它:

std::shared_ptr<Employee> emp = kp::make_shared<Employee>("Karsten", 30);

并在Employee构造函数中:

Employee::Employee(std::string name, int age)
{
  Dept::addEmployee(shared_from_this());
}

在我将其提交给相对较大的代码库之前,我非常感谢您提出的一些想法或反馈。

谢谢!

2 个答案:

答案 0 :(得分:2)

我知道已经有一段时间了,但这可能对遇到同样问题的人很有用:如果您尝试从继承enable_shared_from_this的类中继承,则会出现主要问题。 特别是这一行:

t->_construct_pself = &rtn;

如果您说:

class Object : public kp::enable_shared_from_this<Object> {
};

class Component : public Object {
};

然后,编译器将无法将std::shared_ptr<Component>*强制转换为std::shared_ptr<Object>*,因为即使Component继承了Object,这些类型也不相关。 我看到的最简单的解决方案是像这样将_construct_pself变成void*

template <class T>
    class enable_shared_from_this
    {
    public:
        void* _construct_pself{ nullptr };
        std::weak_ptr<T> _construct_self;
        std::shared_ptr<T> shared_from_this() const
        {
            if (_construct_pself)
            {
                return *static_cast<std::shared_ptr<T>*>(_construct_pself);
            }
            else
            {
                return _construct_self.lock();
            }
        }
    };

然后做

t->_construct_pself = static_cast<void*>(&rtn);

它不是很性感,可能会引起其他问题,但似乎很有效...

[EDIT]有一个更好,更多的“ C ++”替代方案,很抱歉没有立即考虑它,只需执行即可:

t->_construct_pself = reinterpret_cast<decltype(t->_construct_pself)>(&rtn);

[EDIT2]使shared_from_this保持不变,因为它不会更改类中的任何内容

[EDIT3]发现了另一个问题:如果您通过make_shared使用复制构造函数,并且在operator=之前的构造函数中使用shared_from_this,则shared_from_this将返回复制的对象,而不是对象的副本。我看到的唯一解决方案是为enable_shared_from_this定义空的复制构造函数和赋值运算符,并在每次需要时从继承类中显式调用复制构造函数……或者确保您永远不要在{{1 }}放在复制构造函数中。

答案 1 :(得分:1)

我认为在构造函数中使用shared_from_this()存在语义问题。 问题是当抛出异常时没有有效对象,但您已经设置了一个共享指针。 e.g:

Employee::Employee(std::string name, int age)
{
    Dept::addEmployee(shared_from_this());
    if (...) throw std::runtime_error("...");
}

现在Dept将有一个指向此对象的指针,该指针未成功创建。