我们可以使用std :: array的常规指针算法吗?

问题描述 投票:0回答:6

我想弄清楚如何在指向std :: array类的元素的指针上使用旧样式指针算法。以下代码(不出所料)可能无法编译:

int main(int argc, char *argv[])
{
    double* data1 = new double[(int)std::pow(2,20)];
    std::cout << *data1 << " " << *(data1 +1) << std::endl;
    delete data1;
    data1 = NULL;

    double* data2 = new std::array<double, (int)std::pow(2,20)>;
    std::cout << *data2 << " " << *(data2 +1) << std::endl;
    delete data2;
    data2 = NULL;

    return 0;
}

作为练习,我想使用所有传统的指针算法,但不是指向旧式双数组,我希望它指向std :: array的元素。我用这条线思考:

    double* data2 = new std::array<double, (int)std::pow(2,20)>;

是指示编译器data2是指向分配给std::array<double,(int)std::pow(2,20)>的堆的第一个元素的指针。

我被告知double* name = new double[size];完全意味着以下内容:«堆栈为指向一个double的指针分配内存并命名指针name,然后堆分配一个大小为size的双精度数组,然后将指针设置为指向第一个元素由于上面的代码没有编译,我必须被教导一些不正确的东西,因为相同的语法对std :: arrays不起作用。

这提出了几个问题:

  1. type* name = new othertype[size];声明的实际含义是什么?
  2. 如何使用std :: array实现我想要的?
  3. 最后,如何使用std :: unqiue_ptr和std :: make_unique实现相同的目标?
c++ arrays c++11 c++-standard-library
6个回答
7
投票

我被告知double* name = new double[size];的含义完全如下:«Stack为指向ONE double的指针分配内存并命名指针名称,然后堆分配一个大小为double的数组,然后将指针设置为指向第一个元素由于上面的代码没有编译,我必须被教导一些不正确的东西,因为相同的语法对std :: arrays不起作用。

你对这个陈述是正确的,但请记住,这样做的方式是new[]是与new不同的运算符。当您动态分配std::array时,您将调用单个对象new,并且返回的指针指向std::array对象本身。

你可以对std::array的内容做指针运算。例如,data2.data() + 1是指向data2[1]的指针。请注意,您必须调用.data()来获取指向底层数组的指针。

无论如何,不​​要动态分配std::array对象。尽可能避免动态分配,但如果需要,请使用std::vector


4
投票

我们可以使用传统的指针算法与std::array

是的,确定你可以 - 但不是数组本身,这是一个对象。相反,你使用数组中的数据地址,你可以使用std::arraydata() method,如下所示:

std::array<double, 2>  data2 { 12.3, 45.6 };
double* raw_data2 = data2.data(); // or &(*data2.begin());
std::cout << *raw_data2 << " " << *(raw_data2 + 1) << std::endl;

这个compiles and runs fine。但是你可能不需要使用指针算法,并且可以使用std::array的更好的抽象来编写不同的代码。

PS - 避免使用newdelete进行显式内存分配(有关此问题,请参阅C++ Core Guidelines item)。在您的情况下,您根本不需要堆分配 - 就像您不需要使用常规数组一样。


3
投票

您可以使用std::array成员函数访问data()的“原始指针”视图。但是,std::array的观点是你不必这样做:

int main(int argc, char *argv[])
{
    std::array<double, 2> myArray;
    double* data = myArray.data();
    // Note that the builtin a[b] operator is exactly the same as
    // doing *(a+b).
    // But you can just use the overloaded operator[] of std::array.
    // All of these thus print the same thing:
    std::cout << *(data) << " " << *(data+1) << std::endl;
    std::cout << data[0] << " " << data[1] << std::endl;
    std::cout << myArray[0] << " " << myArray[1] << std::endl;

    return 0;
}

1
投票

一般化的含义:

type* name = new othertype[size];

结束是“我需要一个变量name,这是一个指向type的指针,并使用size连续分配othertype new[]实例”。请注意,这涉及到投射,甚至可能不起作用,因为othertypetype可能不支持该操作。 std::arraydouble不等于指向double的指针。这是一个指向std::array,句号的指针,但是如果你想假装它是一个double并且你不介意你的程序由于未定义的行为而崩溃,你可以继续。你的编译器应该在这里警告你,如果没有,你的警告不够严格。

标准库容器都是关于迭代器而不是指针,尤其不是指针算术。迭代器比指针更灵活,更强大,它们可以处理异常数据结构,如链表,树和更多,而不会给调用者带来大量开销。

std::vectorstd::array这样的容器支持“随机访问迭代器”,这是一种直接指针式访问其内容的形式:a[1]等。请仔细阅读任何给定容器的文档,因为有些人允许这样做,许多人不这样做。

请记住,“变量”和“在堆栈上分配”不一定是一回事。优化编译器可以并且将把指针放在任何它想要的地方,包括寄存器而不是内存,或者如果它认为它可以在不破坏代码的表达行为的情况下逃脱它,则根本没有。

如果你想要std::array,你真正做的标准库容器几乎总是比C风格的数组更好:

std::array<double, 2> data2;

如果你需要分享这个结构,你需要考虑使用std::unique_ptr的费用是否值得。这个东西的内存占用空间很小,复制它将是微不足道的,因此使用相对昂贵的内存管理功能毫无意义。

如果您在更大的结构周围传递,请考虑使用引用,并在最中心的数据结构中找到结构,这样就不需要按设计进行复制。


1
投票

当然,这些都是合法的:

template<class T, std::size_t N>
T* alloc_array_as_ptr() {
  auto* arr = new std::array<T,N>;
  if (!arr) return nullptr;
  return arr->data();
}
template<class T, std::size_t N>
T* placement_array_as_ptr( void* ptr ) {
  auto* arr = ::new(ptr) std::array<T,N>;
  return arr->data();
}
template<std::size_t N, class T>
std::array<T, N>* ptr_as_array( T* in ) {
  if (!in) return nullptr;
  return reinterpret_cast<std::array<T,N>*>(in); // legal if created with either above 2 functions!
}
// does not delete!
template<std::size_t N, class T>
void destroy_array_as_ptr( T* t ) {
  if (!t) return;
  ptr_as_array<N>(t)->~std::array<T,N>();
}
// deletes
template<std::size_t N, class T>
void delete_array_as_ptr(T* t) {
  delete ptr_as_array<N>(t);
}

令人震惊的是,如果使用得当,实际上是合法的。指向数组的第一个元素指针可与整个std :: array互换。

您必须自己跟踪数组大小。

我不建议这样做。


1
投票

std::array毕竟是STL容器!

auto storage = std::array<double, 1 << 20>{};
auto data = storage.begin();
std::cout << *data << " " << *(data + 1) << std::endl;
© www.soinside.com 2019 - 2024. All rights reserved.