代码之家  ›  专栏  ›  技术社区  ›  Yubikiri773

当使用具有灵活数组成员(FAM)和非平凡析构函数的类时,如何解决“delete中未知数组大小”错误?[副本]

  •  0
  • Yubikiri773  · 技术社区  · 1 年前
    struct A {
      ~A () {}
    };
    
    struct S {
      S() : i(0) { }
      ~S() {}
      int i;
      
      // This fails:
      //
      // A a[];
      A a[0];
    };
    int main()
    {
        struct A aaa;
        return 0;
    }
    

    请注意,如果将零大小的数组替换为灵活数组成员(FAM) A a[]; ,将出现错误: error: unknown array size in delete . 我从中找到了这个例子 here .

    我在自定义动态扩展容器类中遇到了这个问题。

    我的问题:如何解决?零大小的数组是否是一个有效的替代品并且工作良好? 例如,使用 operator new 为类的实例分配内存并使用 operator delete 以如下方式发布:

    size_t totalSize = sizeof(MyContainer) + capacity * sizeof(MyElement);
    void* memory = operator new(totalSize);
    MyContainer* container = new (memory) MyContainer();
    // ...
    operator delete((void*)container);
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   user17732522    1 年前

    标准C++中不存在灵活的数组成员。它们可能(也可能不)被您的特定编译器支持为语言扩展。如果是这样,它们与其他C++特性的结合使用可能会受到严重限制。

    将柔性阵列成员替换为 A a[0]; 并没有让它变得更好。在标准C++中,数组也不能声明为零大小。同样,如果编译,这将是特定编译器的某种语言扩展。而且,与适当的灵活数组成员相比,它们甚至不是标准的C,这使得语义更加不清楚。

    delete 不可能使用具有非平凡析构函数的类型的灵活数组成员。 删除 需要知道数组的大小,因为它需要调用数组中每个元素的非平凡析构函数。但它无法知道这个尺寸。

    您需要分配内存 operator new / operator new[] 您自己,在获得的内存中创建对象,并放置new或 std::construct_at 并使用显式析构函数调用或调用手动销毁您创建的每个元素 std::destroy_at 。然后,您需要手动释放内存 operator delete / operator delete[] .


    你正试图将C模式应用于C++,而这些模式在C++中没有任何好处。

    实现您想要的东西不需要灵活的数组成员。用户不应该使用 MyContainer* 引用您的容器实例。相反 MyContainer 应由用户按价值使用 MyContainer 应该是一个类类型,其中包含指向动态分配内存的指针。是否 i 成员被关押在 MyContainer 或者在分配指针的开始,由您决定。

    看看标准库实现是如何实现的 std::vector 。您基本上是在尝试用受限的功能集实现同样的东西。 std::矢量 是泛型类型,具有很强的异常安全保证,比您试图做的事情具有更好的算法复杂性,使用泛型分配器而不是仅使用泛型分配器 new / 删除 等等。

    此外,这些实现在分配开始时不存储大小,而是存储在容器对象本身中,但这是一个微小的区别。在C++中正确实现这样一个容器并非易事,需要对对象模型有深入的理解。