【问题标题】:Can i call destructor from its class method?我可以从它的类方法中调用析构函数吗?
【发布时间】:2021-07-14 14:25:43
【问题描述】:

我有一个像下面这样的线程类

class Thread {
public:
  Thread();
  ~Thread();
  void start();
  void stop();
}

所以我需要从 stop() 方法中调用析构函数,这是一个好方法吗?

【问题讨论】:

  • 不,当对象的生命周期结束时会发生什么?描述你需要什么可能会更好(从技术上讲,可能存在一些极端情况,但你想限制你的课程只在那些极端情况下工作吗?)
  • 你可以delete this;它(如果它是一个堆栈变量),但它的风险很大。你为什么要那个?你不能在调用stop 之后delete 对象或使用一些智能指针吗?还是只是堆栈变量?
  • @KirilKirov:Thread 在哪个堆栈上?可能不在它自己的堆栈上,也不在其他线程的堆栈上。
  • @MSalters 就这一点而言,线程与任何其他类没有任何不同。
  • @MSalters - 在两个堆栈上都有。用户可以创建对象Thread(在它的“自己的”堆栈上),然后start 将实际创建操作系统特定的线程(这将在内部创建一个线程特定的堆栈,但这没关系)。线程可以在所有私有数据成员上安全地运行,而不仅是在额外同步的情况下。我错过了什么吗?

标签: c++ destructor


【解决方案1】:

技术上是的,但请注意,您不应再使用已删除的对象、this 和非静态成员:

delete this;

你也可以调用析构函数:

~Thread();

但是,在设计良好的代码中,您可以避免这些类型的自毁。

【讨论】:

  • +1,您可能想在删除语句后添加,而不是使用this 或任何非静态成员变量。
  • @CommanderCorianderSalamander:这个问题意味着必须显式调用析构函数,即因为对象超出范围而没有调用它。
  • 你不能使用 delete this,如果有任何机会对象可能在堆栈上。但是,有时有些对象永远不会也永远不会只是堆栈。我认为这个主题在 C++ FAQ 中
  • 删除自己是一个非常脆弱的位置(即使有效)。将删除委托给管理对象,这样您就不会在删除后意外使用自己的任何部分。
【解决方案2】:

Er'body 说不要从成员函数中调用delete this,而是通知所有者该对象已准备好被删除。

我有一个服务可以创建工人,但在他们被创建后并不关心他们。

从服务类中:

Worker* w;
[...]
while (1) {
  [...]
  w = new Worker();
  [...]
}

然后从工人阶级内部:

void Worker::doWork() {
  [...]
  [...]
  delete this;
}

服务类所做的只是创建新的工作者,所以在工作者类之外没有对工作者的引用,除了指针w,每次创建新工作者时都会丢弃它。

在这种情况下,我认为在工作人员没有更多工作要做时调用delete this 是有意义的(只要delete this;doWork() 中的最后一条语句并且之后没有连续的成员调用doWork().

但是,如果您要存储已创建工作器的每个实例,那么这将不再适用。

【讨论】:

    【解决方案3】:

    您可以使用以下语法:

    ~Thread();
    

    但我怀疑你是否真的需要 C++ 的这个特性。也许你应该更好地设计你的课程。

    显式调用析构函数的一个合法案例是在自定义内存管理器中,您不能使用delete 运算符删除对象。

    【讨论】:

    • 正确的语法,可靠的建议。你很少想像那样破坏自己,人们想要这样做几乎总是想要错误的东西。
    • @RichardPlunkett 在放置新对象管理之外,我很难想出任何人想要这样做的原因。
    • @WhozCraig 同意。在 C++ 规范第 12.4.14 节中:“很少需要显式调用析构函数。这种调用的一种用途是使用带有放置选项的新表达式放置在特定地址的对象。这种显式放置和销毁对象的使用可以是处理专用硬件资源和编写内存管理工具所必需的。”
    • @XiangyanSun 那时我感觉很好,因为那(新的安置情况)是我唯一需要这样做的时候,而且我已经从不必须手动调用它从成员函数代码
    • @WhozCraig:另一种情况比安置新管理层更为罕见。正在销毁(显式调用析构函数)然后重新创建对象(显式使用旧对象位置上的新放置)。 Seen it done 从未使用过它,但这样做是合法的(只要在销毁和重新创建之间不访问对象)。
    【解决方案4】:

    没有。

    我认为从类代码内部调用析构函数是不好的做法。 如果您需要在析构函数中进行清理,您应该使用 cleanup() 函数来封装该工作,并在相关的情况下从停止和析构函数调用该函数。

    显然,这样的解决方案需要保持对象的状态以了解它是否已被清理,以避免不必要的工作和多次释放可能不再属于您的资源。

    特别是对于您的情况,我不确定您为什么要从停止函数中删除线程,如果有某种机制管理线程 - 它应该从外部分配/取消分配线程,而不是线程本身停止时释放自己的内存。 (线程应该执行上面提到的清理,但不强制调用它自己的析构函数)

    【讨论】:

    • 即使cleanup() 与析构函数完全相同?例如我有一些互斥锁要清理,析构函数(和cleanup(),相同),只是尝试检测当前线程拥有哪个互斥锁并解锁它。在某些时候我想做cleanup(),但是当类实例被销毁时,我希望互斥锁被解锁。所以它们共享完全相同的代码。
    【解决方案5】:

    没有。不要这样做。

    让线程归另一个对象所有。

    当您调用stop() 时,线程应该告诉其所有者它已准备好被删除(确保您锁定自己,以便所有者在您完成之前不会删除)。然后让对象的所有者进行适当的清理(希望在不久的将来)。

    【讨论】:

    • 投反对票的人能启发我们其他人吗?这对我来说似乎是完全合理的建议,在大多数情况下这可能会带来更好的代码。
    猜你喜欢
    • 2015-02-14
    • 2014-04-15
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-07
    • 2011-05-05
    相关资源
    最近更新 更多