数据成员之间的构造函数/析构函数顺序依赖关系

时间:2012-11-08 10:52:33

标签: c++ raii

我正在使用libssh2创建一个用于执行SSH命令的C ++类。

libssh2 SSH会话的生命周期经历了以下几个阶段:

  1. 初始化(获取本地资源)
  2. 握手/身份验证(在远程上建立SSH会话) 主机)
  3. 断开连接(终止远程主机上的SSH会话)
  4. 免费(发布本地资源;如有必要,还执行第3步)。
  5. 在第1步之前,我们必须打开一个套接字,我们在步骤2中传递给libssh2。从那时起,我们不再需要传递套接字,因为libssh2将存储对它的引用。在第4步之后,我们可以关闭套接字。

    我通过类(SSHSession)公开了这个,我希望 setup (步骤1和2)发生在ctor和 teardown (步骤3和4)在dtor上发生(因为第2步非常耗时,这将允许我保持一个会话池打开并重用它来执行命令)。

    我的第一次尝试将所有代码都集中在SSHSession上,并且它的ctor很快变得一团糟,“如果这一步失败了,那么我们必须看看已经完成的任务并将其撤消”。 dtor并不复杂,但我仍觉得它“太忙”了。

    然后,我将工作划分为几个类,为每个获取/释放步骤实现RAII,即:

    • 步骤1和4。
    • 步骤2和3。

    我创建了一个实现步骤2和3的类SessionConnection,并且有一个类型为SessionHandle的成员实现了步骤1和4;它还将套接字作为数据成员,在ctor / dtor上创建了第一个顺序依赖 - 在SessionHandle成员之前无法销毁套接字。

    当我考虑我的设计时,我想我可以像这样安排第2步和第3步:

      

    2.1。握手(在远程主机上建立SSH会话)

      2.2。认证

      3.断开连接(终止远程主机上的SSH会话)

    这意味着我可以进一步简化我的SessionConnection类,实现另一个类来执行步骤2.1和3中的RAII,最后得到这样的结果:

    • 班级SSHSession有一个SessionConnection数据成员。
    • 班级SessionConnection实施第2.2步。
    • SessionConnection有一个套接字数据成员。
    • SessionConnection有一个SessionHandle数据成员,它实现了步骤1和4.这必须在套接字之前销毁。
    • SessionConnection有一个RemoteSessionHandle数据成员,它实现了步骤2.1和3.这必须在SessionHandle数据成员之后创建并销毁。

    这极大地简化了我的ctors / dtors,由RAII提供。我发现概念上合理;如果,一方面,我们可以将这些视为SSH会话所处的状态,另一方面,我们也可以将它们视为我们正在管理的资源(本地,远程)。

    但是,我现在在SessionConnection有一个严格的建设/破坏命令,虽然我认为这是一个改进(我在研究中没有发现“这是邪恶的”,但只有“这应该是清楚的”记录“),我对其他意见感兴趣,我很乐意接受有关此设计可能替代方案的信息/指示。

    到目前为止我看过的内容:

    • ScopeGuard,ScopeExit。这是一个类似的机制,我没有找到 我可以用来改进我的设计。
    • 状态机。我找不到用它来简化设计的方法, 它没有解决RAII的一个问题,即如果出现问题就清理。

    感谢您的时间。

1 个答案:

答案 0 :(得分:1)

这种设计似乎没有任何根本性的错误。

如果您想取消对SessionConnection成员的构造/销毁顺序的限制,您可以执行以下操作:

  • 班级SSHSession
    • 实施步骤2.2
    • SSHConnection成员
  • 班级SSHConnection
    • 实施步骤2.1和3
    • LocalSessionHandle成员
  • 班级LocalSessionHandle
    • 实施步骤1和4
    • Socket成员

成员及其包含类的已定义构造/销毁顺序可确保按正确顺序执行步骤。