数据成员之间的构造器/析构器顺序依赖性 [英] Constructor/destructor order dependencies between data members

查看:182
本文介绍了数据成员之间的构造器/析构器顺序依赖性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用libssh2创建一个C ++类来执行SSH命令。<​​/ p>

libssh2 SSH会话的生命周期通过这些阶段:


  1. 初始化(获取本地资源)

  2. 握手/认证(在远程
    主机)

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

  4. 免费(释放本地资源;如有必要,还执行步骤3) / li>

在步骤1之前,我们必须打开一个套接字,我们在步骤2中传递给libssh2。从那时起,需要传递套接字,因为libssh2会存储一个引用。



我通过类( SSHSession )暴露这个问题,并且我想要在ctor上发生 setup (步骤1和2),并且在dtor上发生 teardown (步骤3和4)(因为第2步是耗时的,这将允许我保持一个会话池打开并重用它来执行命令)。



我第一次尝试集中所有的代码在 SSHSession ,它的ctor很快就变成了一团糟,如果这一步失败了,那么我们必须看看已经做了什么并撤消它例程; dtor不复杂,但我仍然发现它太忙。



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




  • 步骤1和4。

  • 步骤2和3。


我创建了一个类 SessionConnection ,它实现了步骤2和3, SessionHandle 实施步骤1和4;它也有套接字作为数据成员,创建对ctor / dtor的第一顺序依赖 - 套接字不能在 SessionHandle 成员之前销毁。



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


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


2.2。验证


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


这意味着我可以进一步简化 SessionConnection 类,实现另一个类在步骤2.1和3上执行RAII,最终结果如下:




  • SSHSession 有一个 SessionConnection 数据成员。

  • c> c> 数据成员。

  • SessionConnection 有一个 SessionHandle 数据成员,必须在套接字之前销毁它。

  • 会话连接有一个 RemoteSessionHandle 数据成员,实现步骤2.1和3.这必须在 SessionHandle 数据成员之前创建并销毁。



这大大简化了我的ctors / dtors,感谢RAII。我发现概念上很健康;如果,一方面,我们可以看看这些作为SSH会话经历的状态,另一方面,我们也可以看到他们作为我们管理的资源(本地,远程)。



然而,我现在在 SessionConnection 中有一个严格的构造/销毁顺序,虽然我相信这是一个改进(我在研究中没有发现表示这是邪恶的,只有这应该清楚地记录),我对其他意见感兴趣,我会很乐意接受有关这个设计的可能替代品的信息/指针。



到目前为止,我所看到的是:




  • ScopeGuard,ScopeExit。这是一个类似的机制,我没有找到
    任何我可以用来改进我的设计。

  • 状态机。我找不到一种方法来简化设计,
    ,它没有解决RAII的一个问题,即如果失败,清理。



感谢您的时间。

解决方案

这个设计似乎没有根本的错误。



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




  • SSHSession

    • 实现步骤2.2

    • SSHConnection 成员

    li>
  • SSHConnection

    • 实施步骤2.1和3

    • LocalSessionHandle 会员


  • > LocalSessionHandle

    • 实现步骤1和4

    • Socket 成员




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


I'm creating a C++ class for executing SSH commands, using libssh2.

The life cycle of a libssh2 SSH session goes through these stages:

  1. Initialization (acquires local resources)
  2. Handshake/Authentication (establishes an SSH session on the remote host)
  3. Disconnect (terminates SSH session on the remote host)
  4. Free (releases local resources; if necessary, also performs step 3).

Before step 1, we have to open a socket, which we pass to libssh2 in step 2. From then on, we don't need to pass the socket anymore, as libssh2 will store a reference to it. After step 4, we can close the socket.

I'm exposing this through a class (SSHSession), and I'd like setup (steps 1 and 2) to happen on ctor and teardown (steps 3 and 4) to happen on dtor (since step 2 is time-consuming, this will allow me to keep a pool of sessions open and reuse it to execute commands).

My first attempt concentrated all the code on SSHSession, and its ctor quickly became a mess, with the "if this step fails, then we must see what has already been done and undo it" routine; the dtor was not as complex, but I still found it too "busy".

Then, I divided the work across several classes, implementing RAII for each acquire/release step, namely:

  • Steps 1 and 4.
  • Steps 2 and 3.

I created a class SessionConnection that implemented steps 2 and 3, and had a member of type SessionHandle that implemented steps 1 and 4; it also had the socket as a data member, creating the first order dependency on ctor/dtor - the socket could not be destroyed before the SessionHandle member.

As I was considering my design, I figured I could arrange steps 2 and 3 like this:

2.1. Handshake (establishes an SSH session on the remote host)

2.2. Authentication

3. Disconnect (terminates SSH session on the remote host)

Which means I could further simplify my SessionConnection class, implementing another class to perform RAII on steps 2.1 and 3, and ending up with something like this:

  • Class SSHSession has a SessionConnection data member.
  • Class SessionConnection implements step 2.2.
  • SessionConnection has a socket data member.
  • SessionConnection has a SessionHandle data member, that implements steps 1 and 4. This must be destroyed before the socket.
  • SessionConnection has a RemoteSessionHandle data member, that implements steps 2.1 and 3. This must be created after, and destroyed before, the SessionHandle data member.

This greatly simplifies my ctors/dtors, courtesy of RAII. And I find conceptually sound; if, on the one hand, we can look at these as states the SSH session goes through, on the other hand, we can also see them as resources (local, remote) we're managing.

However, I now have a strict construction/destruction order in SessionConnection, and while I believe it's an improvement (and I found nothing in my research that stated "this is evil", only "this should be clearly documented"), I'm interested in other opinions, and I'll happily accept info/pointers about possible alternatives to this design.

What I've looked at, so far:

  • ScopeGuard, ScopeExit. It's a similar mechanism, I didn't find anything I could use as an improvement on my design.
  • State Machine. I couldn't find a way to simplify the design with it, and it didn't solve one problem RAII does, namely, cleanup if something fails.

Thanks for your time.

解决方案

There seems to be nothing fundamentally wrong with this design.

If you would like to remove the restriction on the construction/destruction order of the members of SessionConnection, you could do the following:

  • Class SSHSession
    • implements step 2.2
    • has a SSHConnection member
  • Class SSHConnection
    • implements step 2.1 and 3
    • has a LocalSessionHandle member
  • Class LocalSessionHandle
    • implements step 1 and 4
    • has a Socket member

The defined construction/destruction order of members and their containing classes ensures the steps are taken in the right order.

这篇关于数据成员之间的构造器/析构器顺序依赖性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆