2012-06-08 40 views
5

我正在存儲的重要數據結構作爲非結構化字符串,程序定義的分隔符(所以我們需要走串,並提取我們需要的信息的程序的字符串結構我們去),我們想將它轉換爲更加結構化的數據類型。分配包含在一個單一的分配

在本質上,這需要一個領域的結構描述什麼樣的數據結構包含和另一個字段與數據本身的字符串。分配時將始終知道字符串的長度。我們已經通過測試確定加倍爲這些數據類型的需要分配的數量是unnacceptable成本。有沒有什麼辦法來分配結構中包含的結構和std ::字符串的內存在一個單一的分配?如果我們使用cstrings我只是在結構中一個char *和分配的結構和字符串足夠大的塊後,指向該結構的結束,但如果可能的話,我們寧願的std :: string。

我的大部分經驗是C,所以請原諒這裏顯示的任何C++的無知。

+0

如果我理解正確,字符串不能在構建之後增長,並且內存將由管理整個對象的人來管理......因爲您要使用C方式,爲什麼不使用'char *'? C字符串中最糟糕的部分是需要管理內存,但在你的情況下似乎不是問題,是嗎? –

+3

+1有**證明**需要優化之前,所有的半翹! –

+0

如果你真的熱衷於使用'std :: string'而不是c-string,你可以看看使用自定義分配器來實現你想要做的一些事情。這可以讓你在一次分配中彙集你的字符串,但它仍然不會一次性分配一切(即你的結構),它可能最終成爲維護的噩夢。可能最好堅持c弦。 –

回答

1

如果你有這樣嚴格的內存需求,那麼你將不得不放棄std::string

最好的選擇是find或寫一個basic_string_ref (a proposal for the next C++ standard library)的實現,這實際上只是一個char *加上一個大小。但它具有std::basic_string的所有(非變異)功能。然後你使用一個工廠函數來分配你需要的內存(你的結構大小+字符串數據),然後使用placement new來初始化basic_string_ref

當然,您還需要自定義刪除功能,因爲您不能只將指針傳遞給「刪除」。


鑑於previously linked to implementation of basic_string_ref(及其相關的typedef,string_ref),這裏的工廠構造函數/析構函數,對於需要有一個字符串某種類型的T:

template<typename T> T *Create(..., const char *theString, size_t lenstr) 
{ 
    char *memory = new char[sizeof(T) + lenstr + 1]; 
    memcpy(memory + sizeof(T), theString, lenstr); 

    try 
    { 
    return new(memory) T(..., string_ref(theString, lenstr); 
    } 
    catch(...) 
    { 
    delete[] memory; 
    throw; 
    } 
} 

template<typename T> T *Create(..., const std::string & theString) 
{ 
    return Create(..., theString.c_str(), theString.length()); 
} 

template<typename T> T *Create(..., const string_ref &theString) 
{ 
    return Create(..., theString.data(), theString.length()); 
} 

template<typename T> void Destroy(T *pValue) 
{ 
    pValue->~T(); 

    char *memory = reinterpret_cast<char*>(pValue); 
    delete[] memory; 
} 

顯然,你」你需要自己填寫其他的構造函數參數。而你的類型的構造函數將需要一個string_ref引用該字符串。

1

如果您使用的是std::string,您無法真正對結構和字符串進行一次分配,也無法將兩者都分配爲一個大塊。如果你使用舊的C風格的字符串,但它是可能的。

0

實際上,這將需要一個結構,其中包含一個字段,用於描述結構中包含的數據類型,以及另一個字段是數據本身的字符串。

我有一種感覺,你可能沒有在這裏利用C++的類型系統來發揮它的最大潛力。它看起來和感覺非常C-ish(這不是一個合適的詞,我知道)。我沒有具體的例子在這裏發佈,因爲我不知道你想解決的問題。

是否有任何方法來分配內存的結構和包含在結構中的std ::字符串在一個單一的分配?

我相信你是擔心結構分配,然後將字符串拷貝到結構成員?理想情況下這不應該發生(但當然,這取決於你如何以及何時初始化成員)。 C++ 11支持移動建設。這應該照顧你擔心的任何額外的字符串副本。

你應該非常,非常張貼一些代碼,使這個討論值得:)

一個重要的數據結構作爲非結構化字符串,程序定義的分隔符

一個問題:這是字符串可變的?如果沒有,你可以使用稍微不同的數據結構。不要存儲此重要數據結構的部分副本,而是將索引/迭代器存儲到指向分隔符的此字符串中。

// assume that !, [, ], $, % etc. are your program defined delims 
const std::string vital = "!id[thisisdata]$[moredata]%[controlblock]%"; 

// define a special struct 
enum Type { ... }; 
struct Info { 
    size_t start, end; 
    Type type; 
    // define appropriate ctors 
}; 

// parse the string and return Info obejcts 
std::vector<Info> parse(const std::string& str) { 
     std::vector<Info> v; 
     // loop through the string looking for delims 
     for (size_t b = 0, e = str.size(); b < e; ++b) { 
      // on hitting one such delim create an Info 
      switch(str[ b ]) { 
       case '%': 
        ... 
       case '$;:  
       // initializing the start and then move until 
       // you get the appropriate end delim 
      } 
      // use push_back/emplace_back to insert this newly 
      // created Info object back in the vector 
      v.push_back(Info(start, end, kind)); 
     } 
     return v; 
} 
+0

-1:「我相信你擔心結構分配,然後是結構成員的字符串副本?」他正在談論* std :: string中的分配*。 –

+0

@NicolBolas:我還是不明白他在弦內的位置。我相當肯定情況並非如此,因爲字符串是固定寬度的,除非你和我對字符串*有不同的理解。 – dirkgently

+0

他在談論兩個內存分配。顯然這是指包含'std :: string'的類型的構造,以及'std :: string'的字符串內容。 –

1

如果我理解正確的話,你是說通過性能分析,已經確定的事實,你必須分配一個string和數據結構中的另一數據成員強加不可接受的成本,你的應用程序。

如果這是真的,我能想到的一對夫婦的解決方案的情況下。

  1. 在程序啓動之前,您可以預先分配所有這些結構。將它們保存在某種固定的集合中,這樣它們就不是複製構建的,並且在您的string中有足夠的緩衝區來保存您的數據。
  2. 有爭議,因爲它可能看起來,你可以使用舊的C風格char陣列。這似乎是你首先使用string的原因,這就是內存管理。不過就你而言,由於你知道啓動時需要的緩衝區大小,你可以自己處理。如果您喜歡string提供的其他設施,請記住,在<algorithm>中仍有大部分設施可用。
1

看看Variable Sized Struct C++ - 簡單的答案是,在香草C++中沒有辦法做到這一點。

你真的需要在堆上分配容器結構嗎?將它們放在堆棧上可能更有效率,所以根本不需要分配它們。

1

我不確定這是否正好解決您的問題。通過使用預先分配的緩衝區,然後使用'placement new'運算符,可以優化C++中的內存分配。 我試着解決你的問題,因爲我明白了。

unsigned char *myPool = new unsigned char[10000]; 
struct myStruct 
{ 
    myStruct(char* aSource1, char* aSource2) 
    { 
     original = new (myPool) string(aSource1); //placement new 
     data = new (myPool) string(aSource2); //placement new 
    } 
    ~myStruct() 
    { 
     original = NULL; //no deallocation needed 
     data = NULL; //no deallocation needed 
    } 
    string* original; 
    string* data; 
}; 

int main() 
{ 
    myStruct* aStruct = new (myPool) myStruct("h1", "h2"); 

    // Use the struct 

    aStruct = NULL; // No need to deallocate 
    delete [] myPool; 

    return 0; 
} 

[編輯]後,從NicolBolas的評論,這個問題是位更加清晰。我決定再寫一個答案,儘管事實上它不如使用原始字符數組更有利。但是,我仍然認爲這完全在規定的限制之內。 想法是爲SO question中指定的字符串類提供自定義分配器。 在分配方法的實現,使用放置新的

pointer allocate(size_type n, void * = 0) 
{ 
    // fail if we try to allocate too much 
    if((n * sizeof(T))> max_size()) { throw std::bad_alloc(); } 

    //T* t = static_cast<T *>(::operator new(n * sizeof(T))); 
    T* t = new (/* provide the address of the original character buffer*/) T[n]; 
    return t; 
} 

的約束是,對於放置新的工作,原始的字符串地址應該知道在運行時allocater。這可以通過在創建新字符串成員之前通過外部顯式設置來實現。但是,這並不優雅。

+0

這不會有幫助。這隻決定'string'的分配位置,而不是'string'的內容*。 –

+0

問題是「有沒有什麼辦法在一次分配中爲struct和struct中包含的std :: string分配內存?」。這個問題的理由是「將這些數據類型所需的分配數量翻倍是不可接受的成本。」由於內存分配,我認爲性能很差。這裏,避免了分配(對於字符串和結構)。 (我同意,如果字符串操作會降低性能,那麼這還不夠好,但是,這並不明確) – PermanentGuest

+2

不,它不是。 'std :: string'在內部分配內存*。 **這是他想要消除的第二個內存分配。 –

1

根據需要,C風格字符串始終可以轉換爲std::string。實際上,從分析中獲得的觀察結果很可能是由於數據碎片而非簡單的分配數量造成的,並且按需創建std::string將會很有效。當然,不知道你的實際應用程序,這只是一個猜測,並且直到經過測試才真正知道這一點。我想象一類

class my_class { 
    std::string data() const { return self._data; } 
    const char* data_as_c_str() const // In case you really need it! 
    { return self._data; } 
private: 
    int _type; 
    char _data[1]; 
}; 

注意我用了一個巧妙的標準ç伎倆數據佈局:_data是隻要你希望它是,只要你的工廠函數分配它的額外空間。 IIRC,C99甚至給了它一個特殊的語法:

struct my_struct { 
    int type; 
    char data[]; 
}; 

這與C++編譯器工作的好機會。 (這是在C++ 11標準嗎?)

當然,如果你這樣做,你真的需要使所有的構造函數私有和朋友你的工廠函數,以確保工廠函數是唯一的實際實例化my_class的方式 - 如果沒有數組的額外內存,它將被破壞。您肯定也需要製作operator=私密,否則請謹慎實施。


反思您的數據類型可能是一個好主意。

例如,您可以做的一件事是,而不是試圖將您的char數組放入結構化數據類型中,而是使用智能參考。看起來像

class structured_data_reference { 
public: 
    structured_data_reference(const char *data):_data(data) {} 
    std::string get_first_field() const { 
     // Do something interesting with _data to get the first field 
    } 
private: 
    const char *_data; 
}; 

你會想要做的與其他構造函數和賦值運算符正確的事情太(可能禁止分配,並實施一些合理的移動和複製)的類。您可能需要在整個代碼中使用參考計數指針(例如std::shared_ptr)而不是裸指針。


另一種黑客工具,可能是隻使用std::string,但類型信息存儲在第一項(或前幾個)。當然,無論何時訪問數據,這都需要進行覈算。

+0

「按需創建std :: string將是高效的。」怎麼樣?每次你這樣做,你都需要另一塊內存。 –

+0

這真的取決於應用程序。正如我所提到的,性能的損失可能不是因爲std :: string做分配,而是因爲它會分割你的數據。另一方面,創建短暫的'std :: string'臨時對象不會將您的數據分段,並且運氣會很快分配,並在緩存中分配。你甚至可以通過使用像'structured_data_reference :: copy_into_string(std :: string&x)'這樣的方法來避免分配,該方法會用'_data'中的數據副本替換'x'的內容。 – Hurkyl

+0

爲了進一步解釋,你的結構解決方案中的'std :: string'有兩個問題。首先是它提供了2個分配(可能是3個)。其次是你的結構和字符串的內容可能位於內存的不同部分。當後者發生時,每次使用該結構時,都必須觸摸內存的兩個不同部分。由於結構本身很小,如果在內存中沒有任何其他即時有用的數據,並且在某些應用程序中* this *是主要的性能損失,則這是緩存和TLB效率低下的結果 – Hurkyl

1

事實上,兩個分配似乎太高。有兩種方法雖然砍伐:

  • 做單分配
  • 做一個動態分配

這可能似乎不是那麼回事了,所以讓我解釋一下。

1。您可以使用C++

  • struct黑客是的,這不是典型的C++
  • 是的,這需要特別小心

技術上講,它要求:

  • 禁止拷貝構造函數和賦值運算符
  • 構造函數和析構函數private並提供工廠分配和釋放對象的方法

老實說,這是很難的。

2.您可避免分配外struct動態

很簡單:

struct M { 
    Kind _kind; 
    std::string _data; 
}; 

再通M實例在堆棧上。移動操作應保證不復制std::string(您可以始終禁用複製以確保它)。

此解決方案是更簡單。唯一的(輕微的)缺點是內存局部性......但另一方面堆棧的頂部已經在CPU緩存中。