使用析构函数完成任务

Posted

技术标签:

【中文标题】使用析构函数完成任务【英文标题】:Using destructor to finish a task 【发布时间】:2021-10-21 08:30:50 【问题描述】:

我目前正在试验以下方法来最终处理一些数据(伪代码)。

void run_processing(container datas)

    // runs some conversion on data and sends it somewhere.


struct process_item

    container datas;

    process_item(const char* data)
    
        datas.add(data);
    

    ~process_item()
    
        run_processing(datas);
    

    operator <<(const char* data)
    
        datas.add(data);
    


process_item create_item(const char* data)

   process_item item(data);
   // Possible additional calls to setup item.
   return item;

在我的测试中,这允许调用。

create_item("my_data") << "additional data" << "even more data";
// Once this piece executes the destructor for process_item is assumed to be called.

伪代码并没有真正显示出预期的好处,但这不是我的问题。

我正在尝试这个,因为我有一些限制;在输入所有数据之前,无法开始处理,除了 process_item 的范围结束时,不知道何时会出现这种情况。为简单起见,假设在添加最后一个数据后我无法调用其他方法或添加标志。

是否可以假定析构函数在预期时间被调用,或者根据优化级别和编译器选择不同的选项是否太冒险了?

澄清 “在正确的时间”表示下一行之前,调用 create_item 之后的那一行。

【问题讨论】:

当你说“deallocator”时,你可能指的是析构函数,对吧?该标准明确定义了何时调用析构函数,因此编译器或优化级别之间不会有任何差异。 指出了问题的关键。 “在预期的时间” - 你什么时候期待它被调用。 可能是 OT:你知道表达式create_item("my_data") 可以涉及多次调用~process_item() 析构函数吗? NRVO(复制省略类型)不是强制性的。 是的,析构函数总是在你认为的时候被调用。它不像垃圾收集语言。 @Thizzer:在这种情况下,您需要考虑复制和移动构造函数。我建议删除复制 ctor,并实现移动 ctor,以便只有移动到的对象进行处理。 【参考方案1】:

你的策略的一个问题是,在销毁时,你没有(安全的)能力

    抛出异常报错,或者

    返回结果

从析构函数中抛出异常是一个非常非常糟糕的主意,因为如果其他人抛出异常,析构函数会在堆栈展开期间运行,而如果您在堆栈展开期间抛出异常,您的程序就会终止。

有一些方法可以解决它,但它们涉及......在堆栈展开期间不抛出异常。所以任何错误都会丢失。

此外,您可能会遇到异常导致您打算执行的操作部分准备好然后执行的可能性。这可能会破坏数据的一致性。

解决这个问题的一种方法是:

[[nodiscard]] process_item& operator <<(const char* data)

    datas.add(data);
    return *this;

然后提供operator error_code() 或任何强制用户将整个&lt;&lt; 链分配给错误变量的内容。是operator error_code()导致代码被执行。

在此模型下,&lt;&lt; 行上引发的异常不会导致操作运行,因为跳过了 operator error_code()

很遗憾,[[nodiscard]] 不是 c++11。

...

除此之外,是的,临时对象在创建它们的完整表达式结束时被销毁。有一些例外涉及省略和参数传递,但这些不太可能适用于您描述的代码。

由于您编写了析构函数,因此需要遵守规则 5。这意味着您需要删除或实现复制/移动分配/构造函数(删除是简单的选择)。

如果您删除复制/移动操作,您可能会在create_item 中遇到问题,因为不能保证 NRVO 省略。如果您使用 RVO 省略,它将在 c++17 中工作(因为现在可以保证)。另一种选择是让你的类移动友好(包括移动“准备执行”状态),以便移动的实例不会运行操作。

我强烈建议您阻止复制操作。不小心将您想要运行一次的某些操作复制 3 份是相当容易的。能够在中途复制一个操作很酷,但是 C++ 复制语义有点太容易使用:如果您希望能够复制,但又不想意外发生,请添加一个 .copy()const 方法返回一个prvalue。

【讨论】:

【参考方案2】:

glog 做这样的事情,在析构函数中运行进程就可以了。如果你想让 desctuctor 快速运行,请在 anth 线程中运行进程。

LOG(INFO) << "hello" << "world";

LOG(INFO) 是一个创建流对象接收消息的宏,当对象被破坏时,它会将消息写入日志文件。阅读glog的代码可能对你有帮助

【讨论】:

这不能回答问题 只是想对此提供一些帮助,抱歉没有完全理解这个问题。

以上是关于使用析构函数完成任务的主要内容,如果未能解决你的问题,请参考以下文章

9——对象的创建和撤销,构造函数和析构函数

没有足够的时间让析构函数完成?

为啥不能等待 Qt 进程在静态析构函数中完成?

C++:对象和类|| 类的构造函数与析构函数

C++:对象和类|| 类的构造函数与析构函数

C++ 类设计总结回顾------析构函数