【问题标题】:c++ using an array without calling constructorc ++使用数组而不调用构造函数
【发布时间】:2017-03-28 09:49:13
【问题描述】:

我的任务是创建一个模板类型数组而不调用 T(模板)构造函数。之后,我想用 std::move 将一些值从另一个数组移到这个数组。我怎样才能在 C++ 中做到这一点?这是我的代码:

void *temp = malloc((end-begin) * sizeof(T));
for (unsigned int i = begin; i < end; ++i){
    temp[i] = std::move(array[i]);
}

但这行不通。编译器说明如下: 指向不完整类型“void”的指针的下标。

【问题讨论】:

  • 哇,使用带有C++11标签的原始malloc?
  • 寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定的问题或错误以及重现它所需的最短代码在问题本身。没有明确的问题陈述的问题对其他读者没有用处。请参阅:How to create a Minimal, Complete, and Verifiable example
  • 看看placement new

标签: c++ arrays c++11 stdmove


【解决方案1】:

void 的数组毫无意义。 void 是不完整的类型(并且没有大小),因此 temp[i] 会生成您看到的错误。

要实现您想做的事情,为什么不使用std::vector,并在项目可用时将其推送到其中?

std::vector<T> temp;
for (unsigned int i = begin; i < end; ++i){
    temp.push_back(std::move(array[i]));
}

【讨论】:

    【解决方案2】:

    如果您真的想手工完成,您正在寻找placement new。首先你分配一个unsigned chars 的“原始缓冲区”:

    unsigned char* buf = new unsigned char(end-begin);
    

    或:

    auto buf = std::make_unique<unsigned char[]>(end-begin);
    

    然后您编写诸如new (buf) Tnew (buf+sizeof(T)) Tnew (buf+2*sizeof(T)) T 之类的东西来将您的对象“放置”到该内存空间中。

    问题是,在上面的示例中,我完全忽略了对齐要求,这很危险。

    所以,不要复制内存池和std::vector 所做的事情,只需使用std::vector

    std::vector<T> vec;
    vec.reserve(end-begin);
    for (unsigned int i = begin; i < end; ++i){
       vec.push_back(std::move(array[i]);
    }
    

    你就完成了。

    【讨论】:

      【解决方案3】:

      正如其他人指出的那样,void 没有定义大小,因此void* 不能作为数组进行索引。

      temp[1] 从哪里开始?

      一个天真的修复:

      T *temp = std::malloc((end-begin) * sizeof(T));

      与以下组合使用时同样糟糕:

      temp[i] = std::move(array[i]);

      该代码将调用赋值运算符,并且在处理temp[i] 中的垃圾时如果非平凡将失败。例如,如果分配显式或隐式取消分配目标中的资源,它将对未初始化的temp[i] 进行操作并失败。

      如果(如果!)你这样做了,那么正确的代码是:

      std::memcpy(temp+i*sizeof(i),array[I],sizeof(T));

      这行代码本质上是假设T 是一个可简单复制的类型。

      第一行假设T 的对象有一个简单的默认构造函数。 如果T 有一个微不足道的默认构造函数,大多数编译器都会优化元素初始化(这是我假设提问者试图避免的)。

      所以推荐的代码(使用for 循环)是:

      T* temp= new T[end-being];
      for(unsigned int i=begin;i<end;++i){
          temp[i-begin]=array[i];
      }
      

      注意:此代码还修复了原始代码中的一个错误,该错误在begin!=0 时会中断。 该代码似乎将一个子部分从array 的中间复制到temp 的开头,但忘记从temp 的索引0 开始。

      但推荐的 C++ 复制方法是:

      T* temp= new T[end-being];
      std::copy(array+begin,array+end,temp);
      

      良好的实现将尽可能确定并利用任何大容量内存操作。 因此,如果有效,这应该会导致std::memmove(temp,array,(end-begin)*sizeof(T));

      编译器可能无法识别的唯一最终转折是可能稍微更有效

      std::memcpy(temp,array,(end-begin)*sizeof(T));

      在这种情况下实际上是有效的,因为我们知道 temp 不能与 array 重叠(更不用说被复制的范围了)。

      脚注:正如 cmets 中指出的,最传统的 C++ 方法是使用 std::vector,通常可以假定它在幕后使用这些优化。但是,如果由于某种原因无法或不希望重新构建应用程序,则此答案提供了重写所提供代码的有效有效方法。

      【讨论】:

      • 好吧,C++ 方法是使用向量 :)
      • @TheTechel 通常为真。实际上,我们希望 std::vector 能够注意到琐事并使用批量复制并确实利用 std::memcpy!但是std::vector 有(一些轻微的)开销,如果发布者有理由将这些放在一边,这是在不重新编码他们的应用程序的情况下实现结果的正确方法......我会做一个说明。
      【解决方案4】:

      试试

      T *temp = (T*) malloc((end-begin) * sizeof(T));
      

      【讨论】:

      • 或者使用@SanderDeDycker 建议的向量,这样更安全。
      • 这样解决了指针的类型问题,但是如果Tstd::stringtemp[i]处仍然没有字符串,所以试图分配给它是无效的。
      猜你喜欢
      • 2016-03-25
      • 2022-11-21
      • 2021-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-22
      • 1970-01-01
      • 2021-07-19
      相关资源
      最近更新 更多