C++ 在单个分配中分配包含字符串的结构

C++ 在单个分配中分配包含字符串的结构,c++,memory-management,stdstring,C++,Memory Management,Stdstring,我正在开发一个程序,该程序将重要数据结构存储为带有程序定义的分隔符的非结构化字符串(因此我们需要遍历字符串并在执行过程中提取所需信息),我们希望将其转换为更结构化的数据类型 本质上,这将需要一个结构,该结构包含一个描述该结构包含何种数据的字段,以及另一个包含数据本身的字符串字段。字符串的长度在分配时总是已知的。我们通过测试确定,将每种数据类型所需的分配数量增加一倍是不可接受的成本。有没有办法在一次分配中为结构和结构中包含的std::string分配内存?如果我们使用的是cstring,那么在为结

我正在开发一个程序,该程序将重要数据结构存储为带有程序定义的分隔符的非结构化字符串(因此我们需要遍历字符串并在执行过程中提取所需信息),我们希望将其转换为更结构化的数据类型

本质上,这将需要一个结构,该结构包含一个描述该结构包含何种数据的字段,以及另一个包含数据本身的字符串字段。字符串的长度在分配时总是已知的。我们通过测试确定,将每种数据类型所需的分配数量增加一倍是不可接受的成本。有没有办法在一次分配中为结构和结构中包含的std::string分配内存?如果我们使用的是cstring,那么在为结构和字符串分配足够大的块之后,我只需要在结构中有一个char*,并将其指向结构的末尾,但是如果可能的话,我们更喜欢std::string


我的大部分经验是用C,所以请原谅这里显示的任何C++无知。

如果你使用的是<>代码> STD::String ,你不能真正为一个结构和字符串分配一个,也不能使两者的分配成为一个大的块。如果您使用的是旧的C样式字符串,那么这是可能的

本质上,这将需要一个结构,该结构包含一个描述该结构包含何种数据的字段,以及另一个包含数据本身的字符串字段

我有一种感觉,也许你没有充分利用C++的类型系统。它看起来和感觉都很像C-ish(我知道这不是一个合适的词)。我没有具体的例子在这里发布,因为我不知道你试图解决的问题

有没有办法在一次分配中为结构和结构中包含的std::string分配内存

我相信您担心的是结构分配之后的字符串副本到结构成员?这在理想情况下不应该发生(但当然,这取决于初始化成员的方式和时间)。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;
 }
//假设!,[,],$,%等。是您的程序定义的delims吗
常量std::string vital=“!id[thisisdata]$[moredata]%[controlblock]%”;
//定义一个特殊的结构
枚举类型{…};
结构信息{
大小\u t开始、结束;
类型;
//定义适当的系数
};
//解析字符串并返回信息obejcts
std::vector解析(const std::string和str){
std::向量v;
//在字符串中循环查找delims
对于(大小b=0,e=str.size();b
如果我理解正确,您的意思是,通过分析,您已经确定您必须在数据结构中分配一个
字符串和另一个数据成员这一事实给您的应用程序带来了不可接受的成本

如果真是这样,我可以想出几个解决办法

  • 您可以在程序开始之前预先分配所有这些结构。将它们保存在某种固定的集合中,这样它们就不会被复制构造,并且
    字符串中保留足够的缓冲区来保存数据
  • 尽管看起来有争议,但您可以使用旧的C样式
    char
    数组。您似乎忘记了首先使用
    string
    s的大部分原因,这就是内存管理。然而,在您的情况下,因为您知道启动时所需的缓冲区大小,所以您可以自己处理这个问题。如果您喜欢
    string
    提供的其他功能,请记住,
    中仍有许多功能可用
  • 看一看-简短的答案是,在香草C++中没有办法做到。


    您真的需要在堆上分配容器结构吗?将它们放在堆栈上可能更有效,因此它们根本不需要分配。

    我不确定这是否确实解决了您的问题。一种方法是通过使用预先分配的缓冲区,然后使用“放置新”操作符来优化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;
    }
    
    [编辑]在Nicolabolas的评论之后,问题就更清楚了。我决定再写一个答案,尽管在现实中它并没有使用原始字符数组的优势。但是,我仍然认为,这完全在规定的限制范围内。 其想法是为字符串类提供一个自定义分配器,如本文中所指定。 在allocate方法的实现中,使用新的布局作为

    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;
    }
    
    指针分配(大小\u类型n,void*=0)
    {
    //如果我们试图分配太多,就会失败
    如果((n*sizeof(T))>max_size()){throw std::bad_alloc();}
    //T*T=静态_cast(::运算符new(n*sizeof(T));
    T*T=new(/*提供原始字符缓冲区的地址*/)T[n];
    返回
    
    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;
    }
    
    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];
    };
    
    struct my_struct {
        int type;
        char data[];
    };
    
    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;
    };
    
    struct M {
        Kind _kind;
        std::string _data;
    };