【问题标题】:Determine if a method is pure virtual (c++)确定一个方法是否是纯虚拟的 (c++)
【发布时间】:2017-05-24 02:00:26
【问题描述】:

有没有办法在 C++ 中确定一个方法在运行时是否是纯虚拟的?事实上,问题是是否有办法知道派生类的析构函数是否已经执行但基类仍然存在。

这是我的情况(简化):

class BaseClass{
private:
    class ThreadUtil *threadUtil;
public:
    Mutex mutex;
    ~BaseClass(){ 
              threadUtil->Terminate();
              MutexLocker ml(mutex);   // Avoid destruction during use
    }

    virtual Size size()=0;
};

class Derived:public BaseClass{
public:
    Size size()override{return Size(100,80);}   
};


class ThreadUtil{
private:
    bool terminate;
    BaseClass *owner;

public:
    void Run(){
        while(!terminate){
            if (!IS_OWNER_SIZE_FN_PURE_THAT_S_THE_QUESTION){
                MutexLocker ml(owner->mutex);
                DoSomething(owner->size());  // Runtime error if in the dtor of BaseClass 
            }
        }
    }
};

“调用纯虚函数”运行时错误非常偶发(在执行 ~BaseClass 时调用 DoSomething。

在派生类中终止线程+锁定是安全的,但我想在 BaseClass 中执行(特别是如果有很多派生类)。

是否有一种可移植且干净(没有标志)的方式来实现这一点?...或者上述设计有什么问题?

编辑:--------------------

正如一些人所指出的,纯虚拟并不是真正的问题。它正在进入基类的析构函数,线程仍在运行。实际的问题可能应该是,“有没有办法在基类中使用预析构方法?

在 Jeremy Friesner 指出的 Is there any automated way to implement post-constructor and pre-destructor virtual method calls? 中,有一个有趣的想法:

  • 使基类和派生类的析构函数受到保护,因此无法调用 delete。
  • 将 BaseClass 的析构函数设为虚拟。
  • 在基类 Delete() 中实现,它首先终止线程,然后调用析构函数(调用虚拟派生析构函数)

【问题讨论】:

  • 为什么你的基类析构函数不是虚拟的?
  • 你不想从 dtor 调用任何虚拟函数,通常不仅仅是纯函数
  • 纯虚拟在这里是一个红鲱鱼。真正的问题是Run 在对象不再存在时尝试访问它。解决方案是正确管理所有权,例如使用 std::shared_ptr/weak_ptr 而不是原始指针。
  • if 语句会出现与while(!terminate) 相同的问题 - 在您执行测试一纳秒后,条件可能会发生变化。保持锁定是停止非同步更改的标准方法。

标签: c++ destructor pure-virtual


【解决方案1】:

你在这里找错树了——在一个正确的 C++ 程序中,不可能调用一个纯虚函数(因为调用它的尝试会被编译器标记为错误),所以没有需要在运行时判断一个函数是否是纯虚函数。

您有时会收到“调用纯虚函数”错误的原因是因为您的程序有问题 - 特别是,它遇到了竞争条件,您的 Run() 方法正在调用位于被销毁的过程。

你需要做的是确保线程已经退出(通过要求线程退出,然后调用 pthread_join() (或其他等效的 API,这将阻塞直到线程有 100%消失了)之前销毁线程在运行时可能访问的任何对象。只有在线程死了之后才开始清理,这样你就可以避免竞争条件和因此错误/崩溃。

请注意,将 pthread_join() 调用放在 BaseClass 的析构函数中是行不通的,因为当 BaseClass 析构函数运行时,对象的子类层已经被销毁。您需要在删除 BaseClass 为超类的对象之前清理线程。 (当然,在 C++ 中自动化该序列有点尴尬,因为 AFAICT 您必须确保调用者手动调用预删除线程关闭函数;特别是 no easy/automatic/transparent way to automate the generation of the pre-destructor thread-shutdown code

【讨论】:

  • 是的,纯虚拟不是真正的问题。它正在进入基类的析构函数,线程仍在运行。实际的问题可能应该是,“有没有办法在基类中使用预析构方法?”在链接中,有一个有趣的想法: - 将基类和派生类的析构函数设为私有,因此无法调用 delete - 将析构函数设为虚拟。 - 在基类中实现 Delete(),首先终止线程,然后调用析构函数(是虚拟的,因此调用派生析构函数)
【解决方案2】:

是否有一种可移植且干净(没有标志)的方式来实现这一点?...或者上述设计有什么问题?

可移植和干净的方法是防止对象在线程终止之前被破坏。这可以通过 TheradUtil 类通过适当类型的智能指针拥有或共享BaseClass 的所有权来实现。

【讨论】:

    猜你喜欢
    • 2012-04-19
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    • 2017-03-26
    相关资源
    最近更新 更多