-1

对于某些人来说,这可能是一个完全明显的错误,但我似乎无法找出为什么会发生这种分段错误。

我确实了解在访问我的程序不应访问的地址时会发生分段错误。

首先,我有一个名为“UiObject”的基类:

class UiObject {
  private:

  public:
    UiObject();
    virtual void render(char * imageBuffer) = 0;
};

然后,我从 UiObject 类派生多个其他类:例如 ProgressBar 类:

class ProgressBar : public UiObject {
    private:
        int x,y; // Position der ProgressBar auf dem Bildschirm (oben links) (Pixeln)
        int width,height; // Breite und Höhe in Pixeln
    public:
        int maxProgress; // Maximal zu erreichner Fortschritt
        long progress; // Fortschritt aktuell
        ProgressBar(); // Constructor
        void render(char * framebuffer); // Rendermethode
};

现在,这些对象由 UiManager 管理和呈现。函数 renderDisplay() 是使用 std::thread 创建的线程:

void UiManager::renderDisplays() {
  printf("[RENDER] Render thread has been started\n[RENDER] (%x)\n", this);
  while(!this->shouldStop) {
    // Removed code that manages the timing of this function (running approximately 60FPS)
  
    // Rendering:

    for(Display d: displayArray) {
      char * framebuffer = d.getFrameBuffer();
      printf("Framebuffer: %x\n", framebuffer);
      printf("uiArray: %x\n", uiArray);
      printf("&uiArray: %x\n", &uiArray);
      for(UiObject* pObject : uiArray) {
        printf("[RENDER] (%x) Rendering Object...", pObject);
        pObject->render(framebuffer);
      } 
      d.renderDisplay();
    }
  }
}

如您所见,对于每个添加的显示,我都在检索帧缓冲区。我正在打开 /dev/fbX 文件,以便可以直接在屏幕上绘图。这一切都很好。d.getFramebuffer() 返回一个 char * 指针,以便我可以操作像素,然后将其传递给任何 UiObject 的 render(char * framebuffer) 函数。

但是,这个函数调用正在创建一个段错误,我不明白为什么。有时它会工作并一直运行直到线程应该停止,有时它会在第一次渲染函数调用后立即崩溃。

我使用以下方法将 UiObjects 添加到 UiManager:

 ProgressBar pBar;
 pBar.maxProgress = 60*15;
 pBar.progress = 1;

 uiArray.push_back(&pBar);

UiManager 有这个作为它的类定义:

  class UiManager {
  private:
    char ttyfd; // TTY File Descriptor.
    std::thread uiManagerThread;
    std::chrono::system_clock::time_point timeA;
    std::chrono::system_clock::time_point timeB;
    std::chrono::duration<double, std::milli> work_time;

  public:
    std::vector<Display> displayArray;
    std::vector<UiObject*> uiArray;
    bool shouldStop;
    bool displayFPS;

    UiManager();

    void stop();

    void renderDisplays();

    void addDisplay(Display d);

    void startThread();

    void stopThread();
};

现在我想知道为什么会这样。

根据 cppreference https://en.cppreference.com/w/cpp/language/abstract_class及其关于抽象类的文档,我不允许有 =0; 在我的 UiObject 类中。如果我删除它,我将在链接过程中收到编译器警告,并显示消息“未定义对 UiObject 的 vtable 的引用”。

我做错了什么?

我怀疑 std::vector 和 for 循环在 UiManager 中不太有效。

我在 debian 上使用 g++ 作为我的编译器。

我的控制台输出:

[RENDER] Render thread has been started
[RENDER] (6ea609f0)
Framebuffer: e09f0010
uiArray: e09eed20
&uiArray: 6ea60a30
./run.sh: Zeile 21: 25018 Speicherzugriffsfehler  ./a.out

它甚至不跳入 ProgressBar 渲染例程。我还注释掉了渲染函数的内容,因此我只能怀疑特定的函数调用。

4

2 回答 2

2

= 0对于抽象基类是正确的。


然而,

 ProgressBar pBar;
 //...
 uiArray.push_back(&pBar);

不是。

pBar一旦离开它被声明的范围,然后试图在稍后取消引用悬空指针renderDisplays是未定义的行为,它将被销毁。

您需要使用 来创建对象new,或者更好地存储std::unique_ptr<UiObject>在向量中并使用 来创建对象std::make_unique<ProgressBar>()

此外,UiObject需要一个虚拟析构函数:virtual ~UiObject() = default; 否则通过基类指针销毁对象将导致未定义的行为。


然后似乎uiArray在两个线程中的访问之间也没有同步。

在没有同步的情况下在两个线程中访问uiArray以进行修改和读取是数据竞争和未定义的行为。

uiArray在两个线程中访问时,您需要添加一个互斥锁并锁定它。

于 2022-01-29T15:58:01.480 回答
0

我使用以下方法将 UiObjects 添加到 UiManager:

看起来您正在ProgressBar堆栈上分配这些对象。我没有你所有的代码,但我想知道这是否是在另一个函数中完成的,然后在使用它们时堆栈不存在。这可以解释间歇性错误,因为有时该函数调用的堆栈仍然存在。为了解决这个问题,我会ProgressBar在堆上分配对象:

 ProgressBar* pBar = new ProgressBar();
 pBar->maxProgress = 60*15;
 pBar->progress = 1;

 uiArray->push_back(pBar);
于 2022-01-29T16:00:24.037 回答