2009-02-27 77 views
11

我是初學C++程序員,所以我學會了使用數組而不是向量(這似乎是做事的一般方法,然後再轉向向量)。何時使用數組而不是矢量/字符串?

我注意到很多關於SO的答案都建議在數組上使用向量,而在char數組上使用字符串。看起來,這是用C++編寫的「正確」方式。

這一切都說,它仍然值得使用經典的數組/字符*(如果有的話)?

回答

17

在編寫應該在其他項目中使用的代碼時,特別是如果您將目標鎖定在STL可能不存在的特殊平臺(嵌入式,遊戲控制檯等)時。

舊項目或有特殊要求的項目可能不想引入STL庫的依賴關係。一個接口依賴於數組,char *或任何與任何東西都兼容的接口,因爲它是語言的一部分。但是,STL並不保證在所有構建環境中都存在。

3

我建議在編譯時自己知道大小的時候使用數組。儘管可以使用向量,但我們必須記住,向量帶有與在堆上完成的內存分配相關的開銷。如果大小不知道,那麼當然使用向量。

+0

您也可以使用boost ::數組或TR1 ::陣列。 – Anonymous 2009-02-27 12:42:47

+0

複製開銷如何與數組不同?我能想到的地方陣列會更有效率的唯一情況是,如果你有「富ARR [] = {美孚(...),美孚(...),...};」 – 2009-02-27 13:09:50

+0

@Mr Fooz:是的,你是對的。沒有區別。我編輯了我的答案。 – Naveen 2009-02-27 13:41:39

4

當兼容性性能具有非常高的優先級時,Array/char *很有用。向量和字符串是代碼可維護性,可讀性和整體易用性都更好的更高級對象。幾乎總是這樣。

1

我能想到的唯一原因就是速度。您可以對數組/指針類型進行更好的優化,而不是根據對象進行優化。但是如果我絕對知道數據結構需要保存的數據量,我甚至會使用STL。在優化步驟中從STL改爲原始類型比使用難以閱讀的代碼啓動項目要好。

1

我看到兩個方面的原因:

  1. 兼容性(舊代碼,而STL)。
  2. 速度。 (我比較了使用vector/binary_search &數組/手寫二進制搜索的速度,最後得到一個醜陋的代碼(重新分配內存),但是它比第一個快了大約1.2-1.5倍,我用MS VC++ 8 )
15

從來沒有。

如果原始陣列似乎比的載體更好的解決方案(原因其他這裏所述),那麼我使用的std :: TR1 ::陣列或std ::陣列中的C++編譯器11(或boost::array )。 它只是做我會做的檢查,以確保和尺寸值的使用使DRY自動實現(例如我在循環中使用大小,以便將來的數組變化聲明將自動工作)。

這是數組實現「是」的原始數組,其檢查和提供的大小始終保持不變,所以很容易在嵌入式代碼中獲取數組代碼,因爲對於任何編譯器而言都是the code is not really "too smart"。就編譯器支持模板而言,我會在我的代碼中複製boost頭文件,以允許我使用這個代替原始數組。因爲原始數組很容易出錯。 Raw arrays are evil.他們很容易出錯。

它與STL算法(如果可用)非常協調。

現在,有兩種情況下,你需要使用原始陣列(義務):當你在C-唯一代碼(不是C代碼進行通信,但在編寫C-唯一代碼的一部分,就像一個C庫)。但是,這是另一種語言

另一個原因是,當編譯器根本就不支持模板...

6

這個問題實際上可以分爲兩個部分:

  1. 我應該如何爲平板陣列的數據存儲管理?
  2. 我該如何訪問平面陣列的元素?

我個人更喜歡使用std ::矢量用於管理存儲器的情況除外我需要保持與代碼不使用STL兼容性(即,當具有直的C代碼接口)。這是更難做異常安全代碼通過新的或的malloc分配的原始陣列(部分是因爲它真的很容易忘記,你不必擔心它)。有關原因,請參閱RAII上的任何文章。

在實踐中,的std ::矢量被實施爲扁平陣列。因此,總是可以拉出原始數組並使用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; 
} 
1

我就需要訪問結構化數據共享庫工作。這些數據在編譯時已知,因此它使用POD(普通舊數據)結構的文件範圍常量數組來保存數據。

這將導致編譯器和鏈接器將大部分數據在只讀段,有兩個好處:

  • 它可以映射到內存目錄從磁盤不運行任何特殊的初始化代碼。
  • 它可以在進程之間共享。

唯一的例外是編譯器仍然生成初始化代碼來加載浮點常量,因此任何包含浮點數的結構都會以可寫入節結束。我懷疑這與浮動異常或浮點舍入模式有關,但我不知道如何驗證任何假設。

如果我用這個vector和string對象,那麼編譯器會生成更多的初始化代碼,每當我的共享庫被加載,將執行。常量數據將分配在堆上,因此它不會在進程之間共享。

如果我讀了從磁盤上的文件中的數據,我將不得不應付檢查的數據格式,而不是具有C++編譯器爲我做的。我還必須管理一個代碼庫中的數據的生命週期,這個數據庫從一開始就將這個全局數據「燒入」了它。

相關問題