c++ - 链式调用构造函数中 shared_from_this 的问题

标签 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 指针。这只是我稍后想要菊花链的副作用。

一个肮脏的修复可能是我只做了一堆私有(private)的 setX_ 方法来执行实际的分配和其他任务,但不返回任何东西。构造函数调用那些。然后我有公共(public) setX 方法,只需调用私有(private)方法,然后返回 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_ptr 的方法,所以我可以转换为 weak 或在需要时检查 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();
    }
};

最佳答案

我想添加到@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. 该协议(protocol)并不是为了通过调用 set_? 获取指向 ptr_foo 的可存储指针;如果有人这样做,那就是对协议(protocol)的滥用,因为它只适用于链式调用。

关于第 2 点,无论如何您都无法完全关闭协议(protocol)滥用行为。例如,考虑

ptr_foo pf;
ptr_foo *ppf = &pf;

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

关于c++ - 链式调用构造函数中 shared_from_this 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35098984/

相关文章:

c++ - WinAPI 编辑控件不显示换行符

c++ - 使用流的记录器类[多线程]

c# - C#中的多级继承构造函数

java - 使用 'default' HashMap 调用不同的构造函数

c++ - 运行代码时无法输入值

c++ - 使用元编程在编译时初始化函数数组

c++ - 目录校验和

c++ - 在 C++ 中计算一次后跨多个线程共享只读数据

c++ - C++0x lambda 和 operator()、闭包和仿函数之间的区别

java - 构造函数获取一个数组作为参数;如何使用它