【问题标题】:Array of contiguous new objects连续新对象数组
【发布时间】:2011-04-21 17:12:38
【问题描述】:

我目前正在填充元素的向量数组,如下所示:

  std::vector<T*> elemArray;

  for (size_t i = 0; i < elemArray.size(); ++i)
  {
    elemArray = new T();
  }

代码显然已经被简化了。现在在询问另一个question(与此问题无关但与程序相关)后,我意识到我需要一个包含new'd 对象的数组(不能在堆栈上,会溢出,元素太多)但是连续的。也就是说,如果我要接收一个没有数组索引的元素,我应该能够通过执行returnedElement - elemArray[0] 来找到数组索引以获取数组中元素的索引。

我希望我已经解释了问题,如果没有,请告诉我哪些部分,我会尝试澄清。

编辑:我不确定为什么没有调查最高投票的答案。我已经试过很多次了。如果我尝试使用超过 100,000 个(大约)元素分配这样的向量,它总是会给我一个内存错误。其次,我需要指针,从我的示例中可以清楚地看出。突然把它改成不是指针需要大量的代码重写(虽然我愿意这样做,但它仍然没有解决像这样用几百万个元素分配向量不起作用的问题。

【问题讨论】:

  • 如果您希望 objects 是连续的并且您希望其中有 100000 个,那么您可能会用完地址空间。您的对象有多大,您使用的是 64 位平台吗?
  • 虽然我的系统是 64 位,但程序是用 32 位编译的。对象大小为 192 字节,这应该不是问题(大约 20mb 的连续内存)。我有 8GB 的​​系统 RAM。
  • 您可能会感到惊讶,您是否尝试过在程序运行时映射其地址空间?另外,我不太了解您的“我需要指针”要求。如果你有一个连续的元素块,那么构造一个指向任何元素的指针是微不足道的。也许您可以发布一些澄清代码?
  • @Charles:是的,这很简单,但是有很多代码假设容器中充满了需要更改的指针。
  • @Samaursa:如果你在 Windows 上,你可以尝试类似Address Space Monitor (其他工具可用),它以图形方式显示地址空间并显示最大连续空闲区域的大小空间。如果你在 linux 上,你可以cat /proc/self/maps .

标签: c++ arrays vector


【解决方案1】:

std::vector&lt;&gt; 将其元素存储在堆分配的数组中,它不会将元素存储在堆栈中。因此,即使您以简单的方式进行操作,也不会出现任何堆栈溢出:

std::vector<T> elemArray;
for (size_t i = 0; i < elemCount; ++i) {
   elemArray.push_back(T(i));
}

&amp;elemArray[0] 将是指向T 对象的(连续)数组的指针。

【讨论】:

  • 是的——现在看起来更合理了。
  • 我不明白,T(i) 的意思不是构造 T 并将参数传递给 i 吗?
  • @Samaursa:是的,但你的取决于你的T 对象必须如何构造。在那里做任何需要做的事情来创建你的T对象。
  • 当我分配 100,000 多个元素时,我得到了一个错误的分配异常。大约 50,000 左右就可以了……不知道为什么。新的不会发生。此外,我的代码需要进行太多更改才能与 T 而不是 T* 一起使用,所以我更喜欢使用 new。
【解决方案2】:

如果你需要元素是连续的,而不是指针,你可以这样做:

std::vector<T> elemArray(numberOfElements);

元素本身不会在堆栈上,vector 管理内存的动态分配,并且在您的示例中,元素将被值初始化。 (严格来说,从值初始化的临时副本初始化,但这对于有效存储在向量中的对象来说应该是一样的。)

我相信您的索引计算应该是:&amp;returnedElement - &amp;elemArray[0],这将适用于 vector。前提是returnedElement实际上存储在elemArray中。

【讨论】:

    【解决方案3】:

    您的原始循环应如下所示:(尽管它不会在连续内存中创建对象)。

    for (size_t i = 0; i < someSize ; ++i)
    {
        elemArray.push_back(new T());
    }
    

    然后你应该知道两个基本的东西:

    • elemArray.size() 返回当前持有的元素数量 elemArray。这意味着,如果您在 for 循环的条件下使用它,那么您的 for 循环将成为一个无限循环,因为您正在向其中添加元素,因此向量的大小会不断增加。
    • elemArrayT* 的向量,因此您只能存储T*,要填充它,您必须使用push_back 函数。

    【讨论】:

    • “虽然它不会在连续内存中创建对象”,这是我需要的(链接中的另一个问题有一个解决方案,只要我可以完成将对象放置在是连续的并且以某种方式一直是new'd...否则我会为超过 100,000 [我需要 500 万] 的元素得到一个错误的分配异常)
    【解决方案4】:

    考虑到您的旧代码导致堆栈溢出,我认为它可能看起来像这样:

    Item items[2000000]; // stack overflow
    

    如果是这样,那么您可以使用以下语法:

    std::vector<Item> items(2000000);
    

    这将在堆上连续分配(和构造)项目。

    【讨论】:

    • 我需要控制创建它们的时间,并要求向量存储指针......就像我有 int* a[50] 有 50 个指针,我可以通过 sizeof( int) 并且保证是连续的。
    【解决方案5】:

    虽然我也有相同的要求,但出于不同的原因,主要是因为缓存热......(对于少量对象)

        int maxelements = 100000;
    
        T *obj = new T [100000];        
    
        vector<T *> vectorofptr;
    
        for (int i = 0; i < maxelements; i++)
        {
            vectorofptr.push_back(&obj[i]);
        }
    

        int sz = sizeof(T);
        int maxelements = 100000;
    
        void *base = calloc(maxelements, sz); //need to save base for free()
    
        vector<T *> vectorofptr;
        int offset = 0;
    
        for (int i = 0; i < maxelements; i++)
        {
            vectorofptr.push_back((T *) base + offset);
            offset += sz;
        }
    

    【讨论】:

      【解决方案6】:

      分配一大块内存并使用placement new在其中构造你的向量元素:

      elemArray[i] = new (GetNextContinuousAddress()) T(); 
      

      (假设您真的需要指向数组中单独新建对象的指针,并且知道不推荐这样做的原因..)

      【讨论】:

      • 这正是我在这种情况下所需要的,但为什么不推荐呢?
      • 不建议使用指针向量,因为您必须手动删除所有元素。例如,在这样的向量上调用 erase() 会泄漏被删除的指针元素。这就是智能指针的典型用途——它们具有释放被指向对象的析构函数。
      【解决方案7】:

      我知道这是一篇旧帖子,但它可能对任何可能在某个时候需要它的人有用。仅当您牢记一些重要警告时,才可以将指针存储在序列容器中:

      • 您存储的指针指向的对象不会在内存中连续对齐,因为它们是在程序的其他地方(动态或非动态)分配的,您最终会得到一个指向可能分散的数据的连续指针数组跨内存。

      • 由于 STL 容器使用值副本而不是引用副本,因此您的容器不会获得已分配数据的所有权,因此只会在销毁时删除指针对象,而不是它们指向的对象。当这些对象不是动态分配时,这很好,否则您将需要在指向对象的其他地方提供释放机制,例如简单地循环遍历您的指针并单独删除它们或使用共享指针,这将在需要时为您完成工作;- )

      • 1234563指向存储元素的指针在此类操作后仍然有效,否则最终会产生不良副作用...... std::vector 就是这种情况,它会在插入新元素时释放并重新分配额外空间,然后执行移位元素的副本(除非您在末尾插入),从而使元素指针/迭代器无效(尽管某些实现提供了稳定性,例如 boost::stable_vector,但这里的惩罚是您丢失了容器的连续属性,编程时不存在魔法,生活不公平吧?;-))

      问候

      【讨论】:

        猜你喜欢
        • 2017-02-09
        • 1970-01-01
        • 2021-04-29
        • 2012-01-22
        • 2013-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-26
        相关资源
        最近更新 更多