链接调用的构造函数中的shared_from_this问题

时间:2016-01-30 06:54:03

标签: c++ c++11 constructor smart-pointers

我理解为什么shared_from_this在构造函数中不起作用。我在这里发现了一些帖子clearly来解释原因。我也见过workaround。我的问题与此有关,但我正在寻找一个优雅的解决方法。

我非常喜欢这种设计

class Foo : public enable_shared_from_this<Foo> {
public:
  typedef shared_ptr<Foo> Ptr;

  Ptr setA(int) {/* .... */ return this->shared_from_this(); } 
  Ptr setB(int) {/* .... */ return this->shared_from_this(); } 
  Ptr setC(int) {/* .... */ return this->shared_from_this(); } 
};

所以我可以像这样菊花链,我发现它非常易读

Foo::Ptr foo = make_shared<Foo>();
foo->setA(3)->setB(4)->setC(5);

但是,我的setX方法不只是将值赋给属性,它们可能会做稍微复杂的事情(比如设置其他属性等)。所以我想在我的构造函数中使用它们。即。

Foo::Foo(int a, int b, int c) {
    setA(a);
    setB(b);
    setC(c);
}

当然这会崩溃。但是在我的情况下,我实际上并不需要在构造函数中使用共享的 this 指针。这只是我想要菊花链的副作用。

一个脏修复可能是我只是制作了一堆私有的setX_方法,它们执行实际的任务和其他任务,但不返回任何内容。构造函数调用它们。然后我有公共setX方法只需调用私有,然后返回 this - &gt; shared_from_this()。那是安全的,但有点像PITA。是否可以检查 this 的共享指针是否已经存在而不会崩溃?即。

class Foo : public enable_shared_from_this<Foo> {
public:
  typedef shared_ptr<Foo> Ptr;

      Ptr setA(int) { /*.....*/ return getThis(); }

protected:
    Ptr getThis() {
        return safe_to_get_this ? this->shared_from_this : Ptr();
    }

};

更新

我应该在我原来的帖子中说清楚,不要这样做道歉。我使用智能指针的原因并非如此,我可以做菊花链。如果情况允许,我通常会使用参考文献。

但在这种情况下,Foo(和其他类)属于类似结构的图形,它们彼此依赖,有多个父子兄弟关系等。所以我在共享和弱指针之间使用混合。但是我需要返回shared_ptrs的方法,所以我可以强制转换为弱或者在需要时检查null(它们是用工厂方法创建的,而且还有find / get方法,需要检查null)。基本上它是一个物理系统,具有不同类型的粒子和粒子之间的约束。我多年前用自己的智能指针实现编写了这个,现在我将它升级到c ++ 11。我可以让setX方法返回引用,并有一个getThis()方法返回共享指针(这是我以前做的),但后来我担心它变得有些不一致返回智能指针的其他一些方法返回引用。

Foo::Ptr foo = World::create();
foo->setA().setB().setC();

foo = World::find(...);
if(foo) foo->setA().setB();

我更喜欢始终返回指针的一致性,而不是有时指针和有时引用。除非有一个严格的指导方针,可以简洁地概括。

UPDATE2

这是我目前使用的(不是很优雅)解决方法。我只是想知道是否有办法这样做而不用手动跟踪_isInited;

class Foo : public enable_shared_from_this<Foo> {
public:
  typedef shared_ptr<Foo> Ptr;

  Foo() {
     _isInited = false;
     setA();
     setB();
     setC();
     _isInited = true;
  }

  Ptr setA(int) {/* .... */ return getThis(); } 
  Ptr setB(int) {/* .... */ return getThis(); } 
  Ptr setC(int) {/* .... */ return getThis(); } 

protected:
    bool _isInited;

    Ptr() {
       return _isInited ? this->shared_from_this() : Ptr();
    }
};

1 个答案:

答案 0 :(得分:2)

我想通过@JoachimPileborg添加正确的评论。我不认为你的问题与如何使用enable_shared_from_this的方面的技术性有关,而是使用(智能)指针时

考虑以下代码,显示两种使用链式调用的方法:

struct ptr_foo                                                                                                                              
{
    ptr_foo *set_x(int x) { return this; }
    ptr_foo *set_y(int y) { return this; }
};


struct ref_foo
{
    ref_foo &set_x(int x) { return *this; }
    ref_foo &set_y(int y) { return *this; }
};


int main()
{
    ptr_foo pf;
    pf.set_x(0)->set_y(1);

    ref_foo rf;
    rf.set_x(0).set_y(1);

    return 0;
}

第一个类ptr_foo使用原始指针,第二个类ref_foo使用引用。

首先,第二次调用,

rf.set_x(0).set_y(1);

在我看来比第一个更加一致,

pf.set_x(0)->set_y(1);

用于基于堆栈的对象的常见情况。

此外,即使您更喜欢第一个,我也认为没有理由返回智能指针,因为

  1. 没有分配

  2. 该协议不是为了通过调用ptr_foo来获取指向set_?的可存储指针;如果有人这样做,那就是滥用协议,因为它只适用于链式密码。

  3. 关于第2点,无论如何都无法密切关闭协议滥用。例如,考虑

    ptr_foo pf;
    ptr_foo *ppf = &pf;
    

    无论您如何设计ptr_foo,用户现在都拥有一个指向该类型对象的指针,该对象未动态分配,并且可能被错误地擦除。