C++ C++;创建具有可变项目数的结构的方法

C++ C++;创建具有可变项目数的结构的方法,c++,c++11,C++,C++11,我需要创建一个内存区域,其末尾的项目数量可变。我可以这样写: #pragma pack(push,0) struct MyData { int noOfItems; int buffer[0]; }; #pragma pack(pop) MyData * getData(int size) { bufferSize = size* sizeof(int) + sizeof(MyData ::noOfItems); MyData * myData= (MyDa

我需要创建一个内存区域,其末尾的项目数量可变。我可以这样写:

#pragma pack(push,0)
struct MyData
{
    int noOfItems;
    int buffer[0];
};
#pragma pack(pop)

MyData * getData(int size)
{
     bufferSize = size* sizeof(int) + sizeof(MyData ::noOfItems);
     MyData * myData= (MyData *)new char[m_bufferSize];
     return myData;
}
此代码在VS 2015上运行,并警告大小为零的数组不是标准数组

一点搜索显示我是C黑客,C++不支持。


我如何在C++中做到这一点呢?它适用于一般可复制的类型:

template <typename T, typename INT = size_t>
class packed_array {
  public:
    packed_array(INT size) {
      buf_ = new char[sizeof(INT) + size * sizeof(T)];
      memcpy(buf_, &size, sizeof(INT));
    }
    ~packed_array() { delete[] buf_; }

    void set(INT index, T val) {
      memcpy(buf_ + sizeof(INT) + index * sizeof(T), &val, sizeof(T));
    }
    T get(INT index) const {
        T temp;
        memcpy(&temp, buf_ + sizeof(INT) + index * sizeof(T), sizeof(T));
        return temp;
    }

    const char* data() const { return buf_; }

  private:
    char* buf_;

  static_assert(std::is_trivially_copyable<T>::value);
};

int main() {
  int n;
  std::cin >> n;
  packed_array<double, int> a(n);
  for (int i = 0; i < n; i++)
    a.set(i, pow(2.0, i)); 
  for (int i = 0; i < n; i++)
    std::cout << a.get(i) << std::endl;
}
模板
类压缩数组{
公众:
压缩数组(整数大小){
buf=新字符[sizeof(INT)+size*sizeof(T)];
memcpy(buf_u3;和size,sizeof(INT));
}
~packed_array(){delete[]buf_;}
无效集(整数索引,T值){
memcpy(buf_+sizeof(INT)+index*sizeof(T),&val,sizeof(T));
}
T获取(整数索引)常量{
温度;
memcpy(&temp,buf_+sizeof(INT)+index*sizeof(T),sizeof(T));
返回温度;
}
const char*data()const{return buf_;}
私人:
char*buf_;
静态断言(std::是可复制的::值);
};
int main(){
int n;
标准:cin>>n;
压缩_阵列a(n);
对于(int i=0;i模板
结构变量数组在数组末尾{
末端变量数组(标准::大小){
::新((void*)数据())标准::对齐存储;
对于(标准::大小\u t i=0;i0?(无符号)计数:0),
noOfItems(计数)
{}
结构清理{
void运算符()(MyData*ptr){
char*buff=重新解释(ptr);
ptr->~MyData();
删除[]buff;
}
};
公众:
使用up=std::unique\u ptr;
静态向上创建(整数计数){
如果(计数<0)计数=0;
std::unique\u ptr buff=std::make_unique(sizeof(MyData)+sizeof(int)*计数);
auto*ptr=::new((void*)buff.get())MyData(count);
(void)buff.release();
返回(ptr,{});
}
国际noOfItems;
};
MyData*getData(整数大小)
{
return MyData::create(size).release();//危险的非托管内存
}
我相信这是符合标准的,假设您的实现没有在普通类型的数组(比如char)上添加填充。我不知道thta会做什么

我没有假设
MyData
只包含普通的旧数据;您可以用上面的方法将
std::vector
塞进其中。我可能可以用这个假设简化几行

这不仅仅是一点痛苦

auto foo=MyData::create(100)
MyData
创建一个唯一的ptr,该ptr后面有一个100
int
s的缓冲区。
(*foo)[77]
访问缓冲区的第77个元素


由于标准中的一个缺陷,在
MyData
之后没有数组,而是在相邻的内存位置有一个包含100个不同
int
对象的缓冲区。这两个对象之间有很多令人讨厌的差异;naive指针算术保证在数组中工作,但在压缩的相邻
之间不工作>int
s在缓冲区中。我不知道编译器会强制执行这种差异。

我认为它不符合标准。你可以
将某种类型的指针重新解释为不同类型的指针,但取消对此类指针的引用将是未定义的行为。例如,请参见@DanielLangr Which reinterpreet,确切地说,你是ta吗闲逛?我对它们中的大多数都很确定。你可以在
char*
上做指针运算,即使那里没有任何
char
s。如果你看一下构造函数,就会发现有一个
MyData
和一堆
int
s被塞进一个连续的缓冲区,就像我在
MyD中使用placement
new
创建它们一样ata::create
。我唯一怀疑的是
cleanup
中的一个,在那里我销毁
MyData
对象,然后将
char
数组馈送到
delete[]
——我认为
char[]
数组可以在我创建其他对象时保持不变,但我可能错了。嗯,可能是
auto*ptr=::new((void*)buff.get())MyData(计数)
访问
MyData::MyData
中的
buff
的其余部分可能会有问题。这取决于
MyData::MyData
中的
这个
指针是否可以与传递到placement-
new
无效*
指针互换。我明白了,我有点迷失在你的代码中,这相当复杂。为什么不呢您只需分配一个普通字符缓冲区,然后使用placement new来构造元素?唯一需要注意的是正确的对齐(以及异常安全)。然后您就可以通过显式析构函数调用(与
std::vector
的方式相同)来销毁元素@DanielLangr我确实分配了一个纯字符缓冲区——在
create
中。然后我将new
MyData
放置在该缓冲区中。然后它继续在其后面放置新的元素。我想我可以在
create
中创建这些元素,而不是在
var_数组
中;我试图将更多的代码移到paren中T类处理这些细节,但不是很成功,因为 MyDATA//C>仍然很复杂。因为C本质上是C++的子集,所以在低级C++中经常使用C语言习惯用法,尤其是在几乎所有CPU平台上都存在的GCC/G++。
template<class T, class D>
struct var_array_at_end {
  var_array_at_end( std::size_t N ) {
    ::new( (void*)data() ) std::aligned_storage_t<sizeof(T)*N, alignof(T)>;
    for (std::size_t i = 0; i < N; ++i) {
      ::new( (void*)( data()+sizeof(T)*i) ) ) T();
    }
  }
  char* data() { return reinterpret_cast<char*>(this)+sizeof(D); }
  char const* data() const { return reinterpret_cast<char*>(this)+sizeof(D); }
  T* ptr(std::size_t i = 0) { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
  T const* ptr(std::size_t i = 0) const { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
  T& operator[](std::size_t n) {
    return *ptr(n);
  }
  T const& operator[](std::size_t n) const {
    return *ptr(n);
  }
};

struct MyData:
  var_array_at_end<int, MyData>
{
private:
  explicit MyData( int count ):
    var_array_at_end<int, MyData>( count>0?(unsigned)count:0 ),
    noOfItems(count)
  {}
  struct cleanup {
    void operator()(MyData* ptr) {
      char* buff = reinterpret_cast<char*>(ptr);
      ptr->~MyData();
      delete[] buff;
    }
  };
public:
  using up = std::unique_ptr<MyData*, cleanup>;
  static up create( int count ) {
    if (count < 0) count = 0;
    std::unique_ptr<char> buff = std::make_unique<char[]>( sizeof(MyData)+sizeof(int)*count );
    auto* ptr = ::new( (void*)buff.get() ) MyData( count );
    (void)buff.release();
    return up( ptr, {} );
  }
  int noOfItems;
};

MyData * getData(int size)
{
   return MyData::create(size).release(); // dangerous, unmanaged memory
}