你什么时候应该使用数组而不是一个向量/字符串?你什么时候应该使用数组而不是一个向量/字符串?(Whe

2019-05-12 03:09发布

我是一个初学者的C ++程序员,所以我一直在使用数组而不是向量(这个好像是做事情的一般方式,然后转移到载体稍后)的经验教训。

我注意到有很多对SO答案建议使用超过阵列载体和字符串在字符数组。 看来,这是“正确”的方式在C ++编写。

这一切都这样说,如果使用的是经典阵列/炭*(如果有的话),它仍然是值得?

Answer 1:

在编写代码时应该在其他项目中使用,特别是如果你的目标特殊平台(嵌入式,游戏机等),其中STL可能不存在。

旧的项目或有特殊要求的项目,可能不希望对STL库引入的依赖关系。 取决于阵列,烧焦*或因为它是语言的一部分,任何将与任何兼容的接口。 但是STL不保证存在于所有的生成环境。



Answer 2:

决不。

如果一个原始数组似乎比一个载体更好的解决方案(因为其他原因在这里说),那么我使用std :: tr1 ::数组或std ::阵列中的C ++编译器11(或升压::数组 )。 它根本的检查,我无论如何都会做,以确保和大小值的使用会导致干性自动执行(例如我使用的大小循环,使数组声明的未来变化将自动工作)。

这是数组实现“是”原始阵列检查,并提供恒定的大小无论如何,所以很容易获得嵌入代码中的阵列码也因为代码是不是真的“太聪明”的任何编译器。 至于编译器支持模板,我会复制升压头在我的代码,让我用这一个,而不是原始数组。 因为它过于明显,容易使原始阵列错误。 原料阵列是邪恶的 。 他们是容易出错。

并与STL算法(如果可用)很好地工作。

现在,有你需要使用原始阵列(义务)两种情况:当你在C-唯一代码是(不与C代码进行通信,但在C-唯一的代码部分写入,像一个C库)。 但是,那么它的另一种语言

另一个原因是当编译器不支持所有模板...



Answer 3:

这个问题实际上可以分为两个部分:

  1. 我应如何管理扁平阵列数据存储器?
  2. 我应该如何访问平板数组的元素?

我个人更喜欢使用std :: vector的管理内存的情况除外,我需要( 与传统的C代码交互时,IE),以保持与代码不使用STL的兼容性。 这是更难使异常安全的代码通过新的或的malloc分配的原始阵列(部分是因为它真的很容易忘记,你不必担心它)。 查看任何物品RAII的原因。

在实践中, 标准::矢量被实施为扁平阵列 。 因此,它总是能够拉出原始数组,并使用C风格的访问模式。 我通常开始与向量下标运算符的语法。 对于一些编译器,产生一个调试版本时,载体提供自动边界检查 。 这是缓慢的(通常是一个10倍放缓紧密循环),但有帮助找到某些类型的错误。

如果在特定平台上分析表明该操作符[]是一个瓶颈,然后切换到直接访问原始阵列。 有趣的是,根据不同的编译器和OS, 它有时可以更快地使用一个STL矢量不是原始阵列

下面是一个简单的测试应用程序的一些结果。 它与Visual Studio 2008在32位版本模式下使用/ O2优化和Vista x64上运行编译。 类似的结果与64位测试应用程序来实现。

Binary search...
           fill vector (for reference) :  0.27 s
                   array with ptr math :  0.38 s <-- C-style pointers lose
                  array with int index :  0.23 s <-- [] on raw array wins
            array with ptrdiff_t index :  0.24 s
                 vector with int index :  0.30 s  <-- small penalty for vector abstraction
           vector with ptrdiff_t index :  0.30 s

Counting memory (de)allocation...
                memset (for reference) :  2.85 s
      fill malloc-ed raw array with [] :  2.66 s
     fill malloc-ed raw array with ptr :  2.81 s
         fill new-ed raw array with [] :  2.64 s
        fill new-ed raw array with ptr :  2.65 s
                  fill vector as array :  3.06 s  \ something's slower 
                           fill vector :  3.05 s  / with vector!

NOT counting memory (de)allocation...
                memset (for reference) :  2.57 s
      fill malloc-ed raw array with [] :  2.86 s
     fill malloc-ed raw array with ptr :  2.60 s
         fill new-ed raw array with [] :  2.63 s
        fill new-ed raw array with ptr :  2.78 s
                  fill vector as array :  2.49 s \ after discounting the  
                           fill vector :  2.54 s / (de)allocation vector is faster!

码:

#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <string>
#include <vector>
#include <stdio.h>

using namespace std;

__int64 freq; // initialized in main
int const N = 1024*1024*1024/sizeof(int)/2; // 1/2 GB of data
int const nIter = 10;

class Timer {
public:
  Timer(char *name) : name(name) {
    QueryPerformanceCounter((LARGE_INTEGER*)&start);
  }
  ~Timer() {
    __int64 stop;
    QueryPerformanceCounter((LARGE_INTEGER*)&stop);
    printf("  %36s : % 4.2f s\n", name.c_str(), (stop - start)/double(freq));
  }
private:
  string const name;
  __int64 start;
};


template <typename Container, typename Index>
int binarySearch_indexed(Container sortedArray, Index first, Index last, int key) {
  while (first <= last) {
    Index mid = (first + last) / 2; // NOT safe if (first+last) is too big!
    if (key > sortedArray[mid])      first = mid + 1;
    else if (key < sortedArray[mid])  last = mid - 1; 
    else return mid;  
  }
  return 0; // Use "(Index)-1" in real code
}

int Dummy = -1;
int const *binarySearch_ptr(int const *first, int const *last, int key) {
  while (first <= last) {
    int const *mid = (int const *)(((unsigned __int64)first + (unsigned __int64)last) / 2);  
    if (key > *mid)      first = mid + 1;
    else if (key < *mid)  last = mid - 1; 
    else return mid;  
  }
  return &Dummy; // no NULL checks: don't do this for real
}

void timeFillWithAlloc() {
  printf("Counting memory (de)allocation...\n");
  { 
    Timer tt("memset (for reference)");
    int *data = (int*)malloc(N*sizeof(int));
    for (int it=0; it<nIter; it++) memset(data, 0, N*sizeof(int));
    free(data);
  }
  { 
    Timer tt("fill malloc-ed raw array with []");
    int *data = (int*)malloc(N*sizeof(int));
    for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
    free(data);
  }
  { 
    Timer tt("fill malloc-ed raw array with ptr");
    int *data = (int*)malloc(N*sizeof(int));
    for (int it=0; it<nIter; it++) {
    int *d = data;
    for (size_t i=0; i<N; i++) *d++ = (int)i;
    }
    free(data);
  }
  { 
    Timer tt("fill new-ed raw array with []");
    int *data = new int[N];
    for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
    delete [] data;
  }
  { 
    Timer tt("fill new-ed raw array with ptr");
    int *data = new int[N];
    for (int it=0; it<nIter; it++) {
    int *d = data;
    for (size_t i=0; i<N; i++) *d++ = (int)i;
    }
    delete [] data;
  }
  { 
    Timer tt("fill vector as array");
    vector<int> data(N); 
    for (int it=0; it<nIter; it++) {
      int *d = &data[0]; 
    for (size_t i=0; i<N; i++) *d++ = (int)i;
    }
  }
  { 
    Timer tt("fill vector");
    vector<int> data(N); 
    for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
  }
  printf("\n");
}

void timeFillNoAlloc() {
  printf("NOT counting memory (de)allocation...\n");

  { 
    int *data = (int*)malloc(N*sizeof(int));
    {
      Timer tt("memset (for reference)");
      for (int it=0; it<nIter; it++) memset(data, 0, N*sizeof(int));
    }
    free(data);
  }
  { 
    int *data = (int*)malloc(N*sizeof(int));
    {
      Timer tt("fill malloc-ed raw array with []");
      for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
    }
    free(data);
  }
  { 
    int *data = (int*)malloc(N*sizeof(int));
    {
      Timer tt("fill malloc-ed raw array with ptr");
      for (int it=0; it<nIter; it++) {
        int *d = data;
        for (size_t i=0; i<N; i++) *d++ = (int)i;
      }
    }
    free(data);
  }
  { 
    int *data = new int[N];
    {
      Timer tt("fill new-ed raw array with []");
      for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
    }
    delete [] data;
  }
  { 
    int *data = new int[N];
    {
      Timer tt("fill new-ed raw array with ptr");
      for (int it=0; it<nIter; it++) {
        int *d = data;
        for (size_t i=0; i<N; i++) *d++ = (int)i;
      }
    }
    delete [] data;
  }
  { 
    vector<int> data(N); 
    {
      Timer tt("fill vector as array");
      for (int it=0; it<nIter; it++) {
        int *d = &data[0]; 
        for (size_t i=0; i<N; i++) *d++ = (int)i;
      }
    }
  }
  { 
    vector<int> data(N); 
    {
      Timer tt("fill vector");
      for (int it=0; it<nIter; it++) for (size_t i=0; i<N; i++) data[i] = (int)i;
    }
  }
  printf("\n");
}

void timeBinarySearch() {
  printf("Binary search...\n");
  vector<int> data(N); 
  {
    Timer tt("fill vector (for reference)");
    for (size_t i=0; i<N; i++) data[i] = (int)i;
  }

  {
    Timer tt("array with ptr math");
    int sum = 0;
    for (int i=-1000000; i<1000000; i++) {
      sum += *binarySearch_ptr(&data[0], &data[0]+data.size(), i);
    }
  }
  {
    Timer tt("array with int index");
    int sum = 0;
    for (int i=-1000000; i<1000000; i++) {
      sum += data[binarySearch_indexed<int const *, int>(
        &data[0], 0, (int)data.size(), -1)];
    }
  }
  {
    Timer tt("array with ptrdiff_t index");
    int sum = 0;
    for (int i=-1000000; i<1000000; i++) {
      sum += data[binarySearch_indexed<int const *, ptrdiff_t>(
        &data[0], 0, (ptrdiff_t)data.size(), -1)];
    }
  }
  {
    Timer tt("vector with int index");
    int sum = 0;
    for (int i=-1000000; i<1000000; i++) {
      sum += data[binarySearch_indexed<vector<int> const &, int>(
        data, 0, (int)data.size(), -1)];
    }
  }
  {
    Timer tt("vector with ptrdiff_t index");
    int sum = 0;
    for (int i=-1000000; i<1000000; i++) {
      sum += data[binarySearch_indexed<vector<int> const &, ptrdiff_t>(
        data, 0, (ptrdiff_t)data.size(), -1)];
    }
  }

  printf("\n");
}

int main(int argc, char **argv)
{
  QueryPerformanceFrequency((LARGE_INTEGER*)&freq);

  timeBinarySearch();
  timeFillWithAlloc();
  timeFillNoAlloc();

  return 0;
}


Answer 4:

当兼容性性能非常高优先级的磁盘阵列/炭*时非常有用。 向量和字符串是更好的更高级别的对象时,代码的可维护性,可读性和全部测试易于计数。 几乎总是,那是。



Answer 5:

我建议当你知道在编译时间本身的大小使用数组。 虽然载体可以使用,我们必须记住,向量都有其相关的开销上堆进行内存分配。 如果大小是未知的,那么ofcourse使用向量。



Answer 6:

我能想到的唯一理由是速度。 您可以在阵列/指针类型做更好的优化比对,根据对象。 但是,我甚至会使用STL,如果我absoluteltely知道我的数据结构需要容纳的数据量。 从STL到原始类型的优化步骤更改为比更难阅读的代码启动项目更好。



Answer 7:

我看到两个方面的原因:

  1. 兼容性(旧代码,而STL)。
  2. 速度。 (I相比使用矢量/ binary_search&阵列/手写二进制搜索的速度。为了得到(具有存储器重新分配)的最后一个丑陋的代码,但它是快约1.2-1.5倍,然后第一个,我使用MS VC ++ 8 )


Answer 8:

我对需要访问结构化数据共享库工作。 这个数据在编译时是已知的,所以它使用POD(普通旧数据)结构的文件范围的恒定阵列来保存数据。

这使编译器和链接器将大部分数据在只读段,有两个好处:

  • 它可以不运行任何特殊的初始化代码映射到内存目录从磁盘。
  • 它可以在进程之间共享。

唯一的例外是,编译器仍然生成初始化代码来加载浮点常数,因此包含浮点数的任何结构中的可写部分结束。 我怀疑这与浮动例外或浮点舍入模式的东西做的,但我不知道如何验证要么假设。

如果我用向量和字符串对象对于这一点,那么编译器会生成更多的初始化代码,每当我的共享库被加载,将执行。 的常量数据将在堆上分配的,所以它不会是进程之间共享。

如果我读了从磁盘上的文件中的数据,我将不得不应付检查的数据格式,而不是具有C ++编译器为我做的。 我还必须管理已经有这个全局数据从一开始就“烤成”它在一个代码库中的数据的生命周期。



文章来源: When would you use an array rather than a vector/string?