【问题标题】:Do I have to delete pointers to widgets in FLTK?我必须删除指向 FLTK 中小部件的指针吗?
【发布时间】:2021-10-18 14:59:44
【问题描述】:

所以我有以下代码:

int main(int, char **) {
    auto wptr = new Fl_Double_Window{640,480};
    wptr->show();
    Fl::run();
    return 0;
}
> valgrind --leak-check=full -s ./run
==13685== Memcheck, a memory error detector
==13685== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13685== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==13685== Command: ./run
==13685==
==13685==
==13685== HEAP SUMMARY:
==13685==     in use at exit: 741,252 bytes in 1,107 blocks
==13685==   total heap usage: 14,853 allocs, 13,746 frees, 2,829,612 bytes allocated
==13685==
==13685== LEAK SUMMARY:
==13685==    definitely lost: 0 bytes in 0 blocks
==13685==    indirectly lost: 0 bytes in 0 blocks
==13685==      possibly lost: 0 bytes in 0 blocks
==13685==    still reachable: 741,252 bytes in 1,107 blocks
==13685==         suppressed: 0 bytes in 0 blocks
==13685== Reachable blocks (those to which a pointer was found) are not shown.
==13685== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13685==
==13685== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
--13685--
--13685-- used_suppression:      1 X on SUSE11 writev uninit padding /usr/lib/valgrind/default.supp:377
==13685==
==13685== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

请注意,我没有删除指向窗口的指针,并且 valgrind 没有检测到任何泄漏。
我搜索了文档(https://www.fltk.org/doc-1.3/basics.html)以及常见问题解答页面(https://www.fltk.org/doc-1.3/FAQ.htmlhttps://www.fltk.org/articles.php),我发现最新的分组小部件会自动附加新创建的小部件,但我什么也没找到关于 fltk 如何管理内存,例如,是否有一些“垃圾收集器”,或者我是否必须手动删除指针。
通常,此输出意味着存在一些负责释放内存的隐藏机制因为我没有手动释放分配给窗口的内存,而且似乎没有泄漏,但是看看对前面代码的以下修改:

int main(int, char **) {
    auto wptr = new Fl_Double_Window{640,480};
    auto box = new Fl_Box{0,0,100,100,"Hello!"}; // Adding widget
    wptr->show();
    Fl::run();
    return 0;
}
valgrind --leak-check=full -s ./run
==14005== Memcheck, a memory error detector
==14005== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14005== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==14005== Command: ./run
==14005==
==14005==
==14005== HEAP SUMMARY:
==14005==     in use at exit: 1,101,954 bytes in 11,185 blocks
==14005==   total heap usage: 31,631 allocs, 20,446 frees, 7,013,742 bytes allocated
==14005==
==14005== 384 (256 direct, 128 indirect) bytes in 1 blocks are definitely lost in loss record 368 of 540
==14005==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==14005==    by 0x4EC8255: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4ECC200: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4D433BF: ??? (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4D438F5: ??? (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4D43C96: ??? (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4D44080: XftDefaultHasRender (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4D44558: XftDefaultSubstitute (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4D46FD3: XftFontMatch (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4B30739: Fl_Font_Descriptor::Fl_Font_Descriptor(char const*, int, int) (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4B309F2: ??? (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4B337BB: fl_normal_label(Fl_Label const*, int, int, int, int, unsigned int) (in /usr/lib/libfltk.so.1.3.7)
==14005==
==14005== 2,565 (768 direct, 1,797 indirect) bytes in 1 blocks are definitely lost in loss record 482 of 540
==14005==    at 0x484383F: realloc (vg_replace_malloc.c:1192)
==14005==    by 0x4EC830E: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4ED94DA: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4ED156C: FcFontRenderPrepare (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4ED1B9D: FcFontMatch (in /usr/lib/libfontconfig.so.1.12.0)
==14005==    by 0x4D46FEF: XftFontMatch (in /usr/lib/libXft.so.2.3.4)
==14005==    by 0x4B30739: Fl_Font_Descriptor::Fl_Font_Descriptor(char const*, int, int) (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4B309F2: ??? (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4B337BB: fl_normal_label(Fl_Label const*, int, int, int, int, unsigned int) (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4B3390A: Fl_Widget::draw_label(int, int, int, int, unsigned int) const (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4AD8D70: Fl_Group::draw_child(Fl_Widget&) const (in /usr/lib/libfltk.so.1.3.7)
==14005==    by 0x4AD8F92: Fl_Group::draw_children() (in /usr/lib/libfltk.so.1.3.7)
==14005==
==14005== LEAK SUMMARY:
==14005==    definitely lost: 1,024 bytes in 2 blocks
==14005==    indirectly lost: 1,925 bytes in 69 blocks
==14005==      possibly lost: 0 bytes in 0 blocks
==14005==    still reachable: 1,099,005 bytes in 11,114 blocks
==14005==         suppressed: 0 bytes in 0 blocks
==14005== Reachable blocks (those to which a pointer was found) are not shown.
==14005== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==14005==
==14005== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 1 from 1)
--14005--
--14005-- used_suppression:      1 X on SUSE11 writev uninit padding /usr/lib/valgrind/default.supp:377
==14005==
==14005== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 1 from 1)

现在似乎存在内存泄漏。似乎是字体库导致了这种情况。
我将再次修改代码以删除指向小部件的指针:

int main(int, char **) {
    auto wptr = new Fl_Double_Window{640,480};
    auto box = new Fl_Box{0,0,100,100,"Hello!"};
    wptr->show();
    Fl::run();
    delete wptr;
    delete box;
    return 0;
}
> valgrind --leak-check=full ./run
==14238== Memcheck, a memory error detector
==14238== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14238== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==14238== Command: ./run
==14238==
==14238== Invalid read of size 8
==14238==    at 0x1091CA: main (main.cpp:64)
==14238==  Address 0x58a8730 is 0 bytes inside a block of size 120 free'd
==14238==    at 0x48419AB: operator delete(void*, unsigned long) (vg_replace_malloc.c:814)
==14238==    by 0x4AD862D: Fl_Group::clear() (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4AD86E7: Fl_Group::~Fl_Group() (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4AD1889: Fl_Double_Window::~Fl_Double_Window() (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x1091C0: main (main.cpp:63)
==14238==  Block was alloc'd at
==14238==    at 0x483EF3F: operator new(unsigned long) (vg_replace_malloc.c:417)
==14238==    by 0x10915F: main (main.cpp:60)
==14238==
==14238== Jump to the invalid address stated on the next line
==14238==    at 0x0: ???
==14238==    by 0x51D4B24: (below main) (in /usr/lib/libc-2.33.so)
==14238==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==14238==
==14238==
==14238== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==14238==  Bad permissions for mapped region at address 0x0
==14238==    at 0x0: ???
==14238==    by 0x51D4B24: (below main) (in /usr/lib/libc-2.33.so)
==14238==
==14238== HEAP SUMMARY:
==14238==     in use at exit: 1,156,045 bytes in 11,490 blocks
==14238==   total heap usage: 31,586 allocs, 20,096 frees, 7,058,062 bytes allocated
==14238==
==14238== 80 bytes in 1 blocks are possibly lost in loss record 277 of 558
==14238==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==14238==    by 0x53E6D37: ??? (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x53E73EE: __glDispatchInit (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x54600AA: ??? (in /usr/lib/libGLX.so.0.0.0)
==14238==    by 0x400FE2D: call_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x400FF1B: _dl_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x40010C9: ??? (in /usr/lib/ld-2.33.so)
==14238==
==14238== 80 bytes in 1 blocks are possibly lost in loss record 278 of 558
==14238==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==14238==    by 0x53E6D37: ??? (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x53E75D3: __glDispatchRegisterStubCallbacks (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x538E076: ??? (in /usr/lib/libOpenGL.so.0.0.0)
==14238==    by 0x400FE2D: call_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x400FF1B: _dl_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x40010C9: ??? (in /usr/lib/ld-2.33.so)
==14238==
==14238== 80 bytes in 1 blocks are possibly lost in loss record 279 of 558
==14238==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==14238==    by 0x53E6D37: ??? (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x53E75D3: __glDispatchRegisterStubCallbacks (in /usr/lib/libGLdispatch.so.0.0.0)
==14238==    by 0x4A22076: ??? (in /usr/lib/libGL.so.1.7.0)
==14238==    by 0x400FE2D: call_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x400FF1B: _dl_init (in /usr/lib/ld-2.33.so)
==14238==    by 0x40010C9: ??? (in /usr/lib/ld-2.33.so)
==14238==
==14238== 384 (256 direct, 128 indirect) bytes in 1 blocks are definitely lost in loss record 380 of 558
==14238==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==14238==    by 0x4EC8255: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4ECC200: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4D433BF: ??? (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4D438F5: ??? (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4D43C96: ??? (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4D44080: XftDefaultHasRender (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4D44558: XftDefaultSubstitute (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4D46FD3: XftFontMatch (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4B30739: Fl_Font_Descriptor::Fl_Font_Descriptor(char const*, int, int) (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4B309F2: ??? (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4B337BB: fl_normal_label(Fl_Label const*, int, int, int, int, unsigned int) (in /usr/lib/libfltk.so.1.3.7)
==14238==
==14238== 2,565 (768 direct, 1,797 indirect) bytes in 1 blocks are definitely lost in loss record 498 of 558
==14238==    at 0x484383F: realloc (vg_replace_malloc.c:1192)
==14238==    by 0x4EC830E: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4ED94DA: ??? (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4ED156C: FcFontRenderPrepare (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4ED1B9D: FcFontMatch (in /usr/lib/libfontconfig.so.1.12.0)
==14238==    by 0x4D46FEF: XftFontMatch (in /usr/lib/libXft.so.2.3.4)
==14238==    by 0x4B30739: Fl_Font_Descriptor::Fl_Font_Descriptor(char const*, int, int) (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4B309F2: ??? (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4B337BB: fl_normal_label(Fl_Label const*, int, int, int, int, unsigned int) (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4B3390A: Fl_Widget::draw_label(int, int, int, int, unsigned int) const (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4AD8D70: Fl_Group::draw_child(Fl_Widget&) const (in /usr/lib/libfltk.so.1.3.7)
==14238==    by 0x4AD8F92: Fl_Group::draw_children() (in /usr/lib/libfltk.so.1.3.7)
==14238==
==14238== LEAK SUMMARY:
==14238==    definitely lost: 1,024 bytes in 2 blocks
==14238==    indirectly lost: 1,925 bytes in 69 blocks
==14238==      possibly lost: 240 bytes in 3 blocks
==14238==    still reachable: 1,152,856 bytes in 11,416 blocks
==14238==         suppressed: 0 bytes in 0 blocks
==14238== Reachable blocks (those to which a pointer was found) are not shown.
==14238== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==14238==
==14238== For lists of detected and suppressed errors, rerun with: -s
==14238== ERROR SUMMARY: 7 errors from 7 contexts (suppressed: 1 from 1)
Segmentation fault (core dumped)

至少可以说,这种修改只会使情况恶化 XD。
事实上,(如果我没记错的话)“映射区域的错误权限...”暗示已尝试释放内存两次,这表明fltk中确实存在一些内存管理机制。
但后来我发现了这个: How to fix memory leaks in simplest FLTK programm? 这意味着我应该删除指针,所以现在我感到有些困惑:)
我最好的猜测是:
(1) 字体库出现故障和
(2) FLTK 有一些内部垃圾收集器(所以我不应该手动删除小部件指针以免引起任何错误),有人可以确认吗?

【问题讨论】:

    标签: c++ memory-leaks fltk


    【解决方案1】:

    这是答案的 2/3。我必须在 Linux 环境中尝试 FLTK 才能计算出另外 1/3。

    当窗口被删除时,FLTK 将删除子部件。如果你查看 FL_DoubleWindow 的析构函数,它会显示

    /**
      The destructor <I>also deletes all the children</I>. This allows a
      whole tree to be deleted at once, without having to keep a pointer to
      all the children in the user code.
    */
    Fl_Double_Window::~Fl_Double_Window() {
      hide();
    }
    

    我无法解释第二种情况,但在第三种情况下,该框是窗口的子窗口,并在窗口被删除时被删除。因此,当代码删除框时,它正在删除已删除的项目,因此出现错误。

    【讨论】:

    • 是的,如果 Fl_Group 确实是它们的父级,我已经设法到达 Fl_Group 的析构函数删除子小部件指针的部分,如果由于某种原因不是这种情况,那么在 remove(int ) 函数位于位于 Fl_Group 类的析构函数中的 clear() 函数中,array_ 数据成员被简单地重新分配。
    • 但是您是否设法找出最外面的小部件被删除的位置?在本例中为 Fl_Double_Window。我真的很好奇这是如何实现的,因为无论窗口是在堆栈上分配还是在堆上分配(不删除指针),都没有内存泄漏。我设法追踪的是 Fl::run 函数有一个 while (Fl_X::first) wait(FOREVER) 循环。我正在尝试找到“窗口管理器”(fltk.org/doc-1.3/classFl__Window.html#details),因为我猜这就是魔法发生的地方。必须有东西来存储这些 Fl_X 对象,对吧?
    • 我刚刚尝试重新编译所有三种情况(使用 -O0 -g 操作)并运行 valgrind:在堆栈上创建一个 Fl_Double_Window(无泄漏),在堆上创建一个 Fl_Double_Window 而不是删除它(无泄漏),最后在堆上创建一个 Fl_Double_Window 并将其删除(无泄漏)。在所有三种情况下,我还调用了窗口上的 show() 方法。是否可以在运行时检查地址存储在指针中的对象的分配类型???
    【解决方案2】:

    Valgrind 日志清楚地显示 Fl_Double_Windownew Fl_Box 分配的指针没有泄漏,因此手动尝试和 delete 它们是不合理的。

    字体库有“漏洞”;旨在以这种方式工作。这是否是一个好的设计是有争议的,但这就是设计。当它加载字体时,它会一直留在那里,直到应用程序完成。该应用程序可以随时使用任何字体。除了在应用程序退出时,没有释放字体的好地方,而且应用程序退出无论如何都会清除所有内容。应用程序不太可能使用十亿种不同的字体(更可能是 3 到 10 种),因此不存在用未使用的字体填充内存的风险。如果您的应用程序确实使用了十亿种不同的字体,那么您最好根据您的应用程序需求找到一种合理缓存它们的方法;一个简单的字体库不太可能做得很好。

    Exhibit A.

    【讨论】:

    • “不太可能”是允许库泄漏内存的蹩脚借口;D 我明白,当您说“......并且应用程序退出无论如何都会清除所有内容”时。你的意思是释放分配的内存。很明显它没有,或者至少不是根据 valgrind。
    • @Sam 应用程序退出总是清除一切。这就是操作系统通常的工作方式。 Valgrind 向您展示在应用程序退出之前未清除的内容。如果您认为这是一个错误,您可以尝试想办法修复它。
    【解决方案3】:

    我刚刚尝试重新编译所有三种情况(使用 -O0 -g 操作)并运行 valgrind:在堆栈上创建一个 Fl_Double_Window(无泄漏),在堆上创建一个 Fl_Double_Window 并且不删除它(无泄漏),以及最后,在堆上创建一个 Fl_Double_Window 并将其删除(无泄漏)。

    正如其他人所说,valgrind 会在程序退出之前向您显示状态。 valgrind 在 OP 的消息中说“仍然可以访问:1,107 个块中的 741,252 个字节”和“未显示可访问块(找到指针的块)”,这意味着在情况 2(在堆上)valgrind 没有报告“泄漏”,因为仍然有一个指针指向窗口。

    删除窗口(案例 3)并没有太大变化,因为它是在程序结束之前完成的,无论如何它都会释放分配的内存。不同之处仅在于使用delete 运行析构函数,这意味着可以释放其他资源(可以写入缓冲数据,可以关闭或删除文件等)。

    是否应将某些内容视为泄漏取决于运行时的行为。如果您的程序经常在堆上分配窗口和小部件并且不删除它们,那么这构成了内存泄漏(但这不是 FLTK 的错)。 FLTK 保证所有子小部件,例如当父窗口被删除时,一个窗口也被删除。就这样。后台没有运行垃圾收集器。

    【讨论】:

      【解决方案4】:

      但是您是否设法找出最外面的小部件在哪里被删除?在本例中为 Fl_Double_Window。

      FLTK 不会自行删除此类最外层的小部件(通常是窗口)。当这些小部件超出范围(如果在堆栈上分配)或使用运算符 delete(这是程序员的责任)时,它们会被删除。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-08
        • 2020-02-24
        • 2010-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多