【问题标题】:boost::signals2: connect to a slot of a diffeent classboost::signals2: 连接到不同类的插槽
【发布时间】:2021-07-05 17:54:00
【问题描述】:

我正在尝试在我的程序中使用 boost::signals2 功能。 在我的名为“Solid”的“主类”中,我有一个在构造函数主体内初始化的成员(所以在成员初始化列表之后),如下所示:

pointer_M = boost::make_shared<OtherClass>(/*parameters,...*/);

如果信号是由某个函数触发的,我不想调用“OtherClass”的成员(pointer_M 指向的),而是“Solid”的成员,即刚刚初始化 pointer_M 的类。

我尝试了以下方法:

 boost::signals2::connection tria_signal = /*...*/.connect( boost::signals2::signal<void()>::slot_type(&Solid::call_this_function, pointer_M.get()).track(pointer_M) );

“call_this_function”是 Solid 的成员函数。不幸的是,有一堆错误消息。 “OtherClass”和“Solid”没有继承关系。

由于我对 boost 非常缺乏经验,因此我希望能得到一些建议来解决我的问题。

最好的

【问题讨论】:

  • 请编辑您的问题以提供可用于重现问题的minimal reproducible example。还要逐字包含任何错误消息。
  • 不幸的是,我无法提供一个最小的工作示例,因为我使用外部数字库的对象。也许我可以写一段可以重现问题的虚拟代码。但这就是我想要实现的目标吗?
  • 是的,请写“dummycode”。编写该代码的过程很可能会帮助您找到问题,并且至少会使您的问题更清楚。

标签: c++ boost signals2


【解决方案1】:

但那是我想要实现的目标吗?

关键是如果没有更清晰的描述,我们就无法判断。这听起来很简单:信号专门用于解耦调用者和被调用者。

所以让我为你编造你的"dummycode"。我将回避您在该行中显示的巨大的类型过度复杂性:

boost::signals2::connection tria_signal =
/*...*/.connect(boost::signals2::signal<void()>::slot_type(
                &Solid::call_this_function, pointer_M.get())
                .track(pointer_M));

槽的整个想法是它们使用类型擦除来概括可调用对象。只需以任何兼容的形式提供您的可调用对象,然后让库推断出神秘的实现类型。

Live On Coliru

#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>

struct OtherClass {
    OtherClass(...) {}

    boost::signals2::signal<void()> tria;

    void test() {
        if (!tria.empty())
            tria();
    }
};

struct Solid {
    boost::shared_ptr<OtherClass> pointer_M;

    Solid() {
        pointer_M = boost::make_shared<OtherClass>(1,2,3);
        auto tria_signal = pointer_M->tria.connect(
            boost::bind(&Solid::call_this_function, this));
    }

  private:
    void call_this_function() {
        std::cout << "Solid was here" << std::endl;
    };
};

int main() {
    Solid s;
    s.pointer_M->test();
}

打印

Solid was here

水晶球:我们能猜出问题吗?

也许我们可以猜到问题所在:看起来您正在努力跟踪pointer_M 指向的对象的生命周期。这没什么用,因为OtherClass 首先拥有信号,这意味着当OtherClass 消失时,所有连接都会断开。

正确的生命周期管理

您可能想要的是在Solid 对象的生命周期结束时断开连接,不是 OtherClass。一般来说,我建议在这里使用scoped_connection:

Live On Coliru

#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>

struct OtherClass {
    OtherClass(...) {}

    boost::signals2::signal<void()> tria;

    void test() {
        if (!tria.empty())
            tria();
    }
};

struct Solid {
    boost::shared_ptr<OtherClass> pointer_M;

    Solid() {
        pointer_M = boost::make_shared<OtherClass>(1,2,3);
        tria_signal = pointer_M->tria.connect(
            boost::bind(&Solid::call_this_function, this));
    }

  private:
    boost::signals2::scoped_connection tria_signal;

    void call_this_function() {
        std::cout << "Solid was here" << std::endl;
    };
};

int main() {
    boost::shared_ptr<OtherClass> keep;
    {
        Solid s;
        std::cout << "Testing once:" << std::endl;
        s.pointer_M->test();

        keep = s.pointer_M; // keep the OtherClass alive
    } // destructs Solid s

    std::cout << "Testing again:" << std::endl;
    keep->test(); // no longer connected, due to scoped_connection
}

打印

Testing once:
Solid was here
Testing again:

简化

在您的情况下,OtherClass 已经由 Solid 拥有(至少它是创建的)。似乎根本不需要共享指针:

Live On Coliru

#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>

struct OtherClass {
    OtherClass(...) {}

    boost::signals2::signal<void()> tria;

    void test() {
        if (!tria.empty())
            tria();
    }
};

struct Solid {
    Solid() : oc_M(1,2,3) {
        tria_signal = oc_M.tria.connect(
            boost::bind(&Solid::call_this_function, this));
    }

    void test() { oc_M.test(); }

  private:
    OtherClass oc_M;
    boost::signals2::scoped_connection tria_signal;

    void call_this_function() {
        std::cout << "Solid was here" << std::endl;
    };
};

int main() {
    Solid s;
    s.test();
}

因为成员是按照声明的相反顺序销毁的,所以这是完全安全的。

建筑航天

如果您知道自己在做什么,并且 pointer_M 实际上需要共享,那么您可能想要跟踪 that 指针。您可能应该考虑制作Solid 和enable_shared_from_this。如果您想成为真正的“企业级工程师™”,您也许可以使用别名构造函数做一些花哨的事情:What is shared_ptr's aliasing constructor for?

【讨论】:

  • 非常感谢您详细的回答! :-) 这个难题的答案是,实际上 Solid 是信号的所有者(实际上是 Solid 的成员变量)。因此,我想跟踪指向其他类的指针的生命周期。顺便说一句,我可以通过用“this”替换“pointer_M.get()”来修复我发布的代码,因为我想连接 Solid 的成员函数。我猜你是对的;我不会一个shared_ptr。我的 pointer_M 实际上是指向其他类对象的唯一指针。所以 unique_pointer 会是更好的选择,是吗?
  • 我建议已经有成员已经表达了所有权。因此,如果生命周期不应与 Solid 实例 100% 重叠,则可以使用唯一指针,但在这种情况下考虑更具表现力的容器,例如 std::optional&lt;&gt;。
  • 我通常会使用一个简单的数据成员来表达所有权关系。如果生命周期需要改变,那么unique_ptr 可能是有意义的,尽管我认为std::optional&lt;&gt; 表达惰性生命周期语义而不暗示动态分配。
  • 哦。天哪。我在另一个标签中写了该评论。我以为我丢了它:)
  • 对此还有一个问题: boost::shared_ptr 是否“意识到”他指向的对象可能发生的变化?假设 boost 指针指向一个存储变量地址的对象,该变量也是实体实例的成员。现在实体实例改变了这个成员(可能也是它的地址)。升压指针是否也会自动更新此地址,还是两个实例中该成员的地址现在不同?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-22
  • 2012-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多