【问题标题】:Virtual method & this pointer虚方法和this指针
【发布时间】:2010-10-01 00:12:57
【问题描述】:

我刚刚开始学习 C++,我正在尝试制作具有 Java Thread 类的基本功能的 Thread 类。我要做的是创建一个子类,编写一个 Run 方法(在基类中是纯虚拟的)创建一个子类的对象,调用它的 start 方法,然后你就有线程了。

问题在于,在我使用 C++ 的方式中,调度没有正确完成 - 就像 Run 函数不是虚拟的,而是调用了基类的 Run 方法。

这是标题的代码

#ifndef _THREAD_H_
#define _THREAD_H_

#include <pthread.h>

class Thread {
 public:
  Thread();

  void Start();

  ~Thread();

 protected:
  virtual void Run() = 0;

 private:
  static void *RunWrapper(void *);

  pthread_t thread;
};

#endif

实现

#include "thread.h"

#include <pthread.h>

Thread::Thread() {
}

void Thread::Start() {
  pthread_create(&thread, NULL, Thread::RunWrapper, (void *) this);
}

void *Thread::RunWrapper(void *arg) {
  Thread *t = (Thread *) arg;
  t->Run();
  return arg;
}

Thread::~Thread() {
  pthread_join(thread, NULL);
}

以及实际尝试做某事的文件

#include <iostream>

#include "thread.h"

class MyThread : public Thread {
 protected:
  void Run() {
    std::cout << "The thread is runned" << std::endl;
  }
};

int main(void) {
  MyThread thread;
  thread.Start();
  return 0;
}

我在过去 10 小时内不断收到的错误是:

pure virtual method called
terminate called without an active exception

【问题讨论】:

  • 如果您真的想复制 Java 功能,请复制 java.util.concurrent 包。它具有更高级的多线程概念,例如执行器、期货、锁和互斥锁。但是,如果您要走那么远,只需使用 boost:它已经实现了所有这些东西。

标签: c++ linux pthreads


【解决方案1】:

问题是您在 main 中的 MyThread 对象在 main 函数返回后立即被销毁,这可能发生在新线程真正开始调用其 Start 方法之前。

作为销毁过程的一部分,在调用基类析构函数之前,vtable 将被重置为基类的 vtable,因此当 RunWrapper 调用稍后运行时,它最终会触发纯虚方法错误。在被破坏的对象上调用方法会导致未定义的行为,因此任何事情都可能发生;这种行为是您的 C++ 编译器如何实现析构函数和堆栈分配的意外。

【讨论】:

  • 是的,这就是问题所在。非常感谢!当学习一门新语言时,人们认为所有的问题都是由语言产生的:)。
【解决方案2】:

不确定是什么直接导致了错误,但您的做法很糟糕:您的 MyThread 对象可能会在您的线程访问它之前超出范围。完全有可能在您的线程开始处理时,范围已经消失并且指针无效。

尝试在堆上分配你的对象,看看它是否有效(然后,假设它有效,找出线程完成后如何释放对象)。

哦,接下来你会想要退出你的应用程序(通过从 main 返回),直到你的线程也完成......

【讨论】:

  • 我使用同步机制修复了它。谢谢!
  • 好吧,它不会完全消失,因为在Thread::~Thread() 中调用了pthread_join。但是当Thread::~Thread运行时,对象已经失去了它作为派生类的身份(派生析构函数已经运行)——在销毁过程中,虚拟方法被非虚拟调用。
【解决方案3】:

问题是你正在调用 Start,它属于 Thread 类。然后这个方法调用Run,它会调用Thread类的Run方法。不幸的是,您不能从基类调用被覆盖的方法。

【讨论】:

  • 您不能从基类方法中调用它们。
  • 我可以在子类上调用 Run 因为我使用的是指针并且方法是虚拟的。
  • 似乎可以。那你为什么会收到虚拟方法错误?
猜你喜欢
  • 2011-06-19
  • 2017-06-09
  • 2016-02-22
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
相关资源
最近更新 更多