使用析构函数做工作

Use the destructor to do work

我有一个包含一组请求的 class 事务。 每X秒设置当前事务中的请求,并清除请求列表。

是否可以将 class 事务设计为在析构函数销毁期间发送事务中的请求?

这意味着我们分配一个新的事务,只要它还活着,就向它添加新的请求,一旦调用析构函数,所有的请求都会被发送。

这样我们可以保证:

  1. 发送所有交易(只要我们不泄露交易对象)。
  2. 交易发送后无法添加更改。

这被认为是一种好的做法吗?或者使用 SendRequests 方法发送所有请求并清除列表会更好吗?

析构函数在 C++ 语言中只有一个用途。那就是根据 RAII 管理(因此它可以记录、使用辅助对象或函数,)释放在构造函数中获取的资源。任何其他用途迟早会给您带来麻烦。

你通常会做恰恰相反的事情——对积极分支(导致提交的分支)明确,但回滚分支 RAII-based 以保证一致的 commit-or-rollback 行为。主要是因为从析构函数中抛出存在问题,并且实施 no-throw 回滚通常比对提交执行相同的回滚更容易。

您应该只在 class.

的析构函数中执行实际解构手头对象的操作

析构函数仅用于 clean-up 任务 仅与当前对象有关 。在这种对象的end-of-life周期触发对外通信,肯定会给你带来麻烦。

一个示例问题发生在子classing 和多态性过程中:

#include <iostream>

class A {
public:
  A() {
    std::cout << "Construct A" << std::endl;
  }

  virtual ~A() {
    std::cout << "Deconstruct A" << std::endl;
    this->doWork();
  }

  virtual void doWork() {
    std::cout << "Dummy virtual worker function in A" << std::endl;
  }
};

class B : public A {
public:
  B() {
    std::cout << "Construct B" << std::endl;
  }

  virtual ~B() {
    std::cout << "Deconstruct B" << std::endl;
  }

  void doWork() {
    std::cout << "Actual worker function in B" << std::endl;
  };
};

int main(int argc, char** argv) {
  A* aTest = new B();
  aTest->doWork();

  delete aTest;

  return 0;
}

这导致输出

Construct A
Construct B
Actual worker function in B
Deconstruct B
Deconstruct A
Dummy virtual worker function in A

因此,当您子class 您的 class 并覆盖其中的虚函数时,当您到达基础 class 的析构函数时,您将失去被覆盖的功能。在示例中,当在 A 的析构函数中调用 this->doWork() 时会发生这种情况。

这可能会弄乱您的数据、流程或您 class 正在做的任何事情。因此,我再次建议不要在析构函数中触发实际工作。最好在 class 中为此定义一个单独的函数,并在为此特定任务指定的时间调用它。否则你的代码只会失去很多可读性和可维护性。

一方面,技术可能性:

12.7/4: Member functions, including virtual functions, can be called during construction or destruction.

另一方面,每一个设计都应该尊重的指导原则:

12.4/15 Once a destructor is invoked for an object, the object no longer exists;

因此我不建议这样的设计。

这样的设计是不好的做法。如果创建了一个事务,并向其中添加了一些请求,如果出于任何原因需要中断事务(连接的系统消失,用户想要中断,发生异常等),您的设计将强制要执行的不完整请求。这不符合人们对交易的全部或全部期望的逻辑。

更好的方法是根据 state design pattern 设计您的交易:例如,交易将具有以下状态:

  • 已创建(新的,无请求),
  • 正在进行(正在添加请求),
  • to-be-executed(不会执行额外的请求),
  • 并已完成(请求已执行)或已取消。

如果调用了析构函数,状态既没有完成也没有取消,析构函数应该争取取消。