【问题标题】:Problems with shared_from_this in Constructor for Chained Calls链式调用的构造函数中 shared_from_this 的问题
【发布时间】:2023-03-05 14:29:01
【问题描述】:

我明白为什么 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->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();

我只是更喜欢始终返回指针的一致性,而不是有时指针有时引用。除非对此有严格的指导方针,可以简明扼要地总结。

更新2

这是我目前使用的(不是很优雅的)解决方法。我只是想知道是否有一种方法可以在不手动跟踪 _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();
    }
};

【问题讨论】:

  • 如果你只想将成员函数调用链接在一起,为什么要使用指针开头呢?只需返回对类本身的引用,您不必担心 shared_from_this 等。我建议您从 资源所有权 的角度来看智能指针,而不仅仅是简单的指针会被自动删除。

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


【解决方案1】:

我想补充@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. 该协议并非旨在通过调用set_? 来获取指向ptr_foo 的可存储指针;如果有人这样做,那就是对协议的滥用,因为它只是用于链式调用。

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

ptr_foo pf;
ptr_foo *ppf = &pf;

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

【讨论】:

  • 是的,我同意你所说的,但为了简洁起见,我从我的问题中省略了关于我为什么要使用智能指针的信息。我现在已经更新了问题。
猜你喜欢
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
相关资源
最近更新 更多