是否可以使用零长度数组成员编写"完整"C++类?

Is it possible to write a "complete" C++ class with Zero Length Array member?

本文关键字:完整 C++ 组成员 可以使 数组 是否      更新时间:2023-10-16

我有一些数据类型,如果我使用普通的旧C,它将实现为:

typedef struct {
    ...many other members here...
    unsigned short _size;
    char           _buf[0];
} my_data; 

我想做的,基本上是把它变成一个,并添加常用的运算符,如less,相等,复制构造函数运算符赋值。你可以想象,我会在像 std::map 这样的关联容器中使用这样的作为它的

理想情况下,我需要缓冲区与对象本身处于同一级别,否则当我必须比较其中两个(缓冲区)时,我会让 CPU 获取指针并将其加载到内存中;我不想使用 std::vector,因为分配的内存不会与其他数据成员连续

对我来说的主要问题是,在 C 中,我将有一个函数,给定缓冲区的大小,它将为其分配适当的内存大小。在C++这样的事情是做不到的。

我说的对吗?干杯

这是完全不可能的。您的对象实际上是可变大小的,但std::map将始终将其视为固定大小,并且无法实现复制或移动。你需要一个旧的 C 风格的容器来使用这样的技巧。

编辑:自定义分配器。有趣的解决方案,我没有想到。我不知道你是否能让它工作,但值得研究。

不,这是不可能的。零长度数组不是合法C++。

您可以1 对长度为 1 的数组执行非常相似的事情,但您仍然必须自己管理实例的创建,因此没有复制构造函数,也没有将对象存储在 std::map 中。

也许是这样的:

class my_data {
public:
  static my_data* create(int size) {
    void* memory = malloc(sizeof(_size) + size);
    return new(memory) my_data(size);
  }
  static void destroy(my_data* ptr) {
    ptr->~my_data();
    free(ptr);
  }
private:
  //disable construction and destruction except by static methods above
  explicit my_data(int size) : _size(size) {}
  ~my_data(){}
  //disable copying
  my_data(const my_data&);
  my_data& operator=(const my_data&);
  unsigned short _size;
  char           _buf[1];
};

请注意,默认构造函数、析构函数、复制构造函数和赋值运算符都是私有的,这极大地限制了类的使用方式。


1 实际上 - 它不符合标准,但它几乎可以在任何地方工作。

您可以在C++中以这种方式处理它:

struct MyData {
    unsigned short size_;
    char * buf () { return reinterpret_cast<char *>(&size_ + 1); }
    //...
};

您可能希望重载new运算符以使用mallocdelete使用free,以便您可以使用realloc按需扩展数据结构。

正如DeadMG指出的那样,这不能很容易地与STL容器一起使用。一种方法是使用指向容器中MyData的指针。另一个是用于MyData的自定义智能指针包装器类。

编辑:这是一个黑客,MyData充当一种智能指针,但情报由vector管理。

struct MyData {
    struct State {
        unsigned short size_;
        //...
    };
    std::vector<State> data_;
    MyData () {};
    MyData (unsigned short size)
        : data_(1 + size/sizeof(State) + !!size%sizeof(State)) {
        data_[0].size_ = size;
    }
    unsigned short size () const { return data_[0].size_; }
    //...
    char * buf () { return reinterpret_cast<char *>(&data_[1]); }
};