繁体   English   中英

C ++内存管理和向量

[英]C++ memory management and vectors

我对与向量有关的内存管理感到非常困惑,可以用一些基本的概念来解释。

我有一个使用大向量的程序。 我使用new运算符创建了向量,并在程序结束时使用delete释放它们以获取内存。

我的问题是,如果程序因任何原因崩溃或中止, 删除行将被遗漏,是否有办法恢复内存,即使在这种情况下。

我还有一些其他大的向量,我没有new关键字。 我已经读过这些将在堆上创建,但无论如何都不需要解除分配,因为内存管理是在“引擎盖下”处理的。 但是我不确定是这种情况,因为每次运行我的程序时我都会失去RAM。

所以我的第二个问题是,没有new关键字创建的向量是否真的留给他们自己的设备,并且即使代码在流程中被中止,也可以信任自己清理。

我想刚刚想到的第三个问题是,如果在堆上自动创建了Vectors,你为什么要在它们中使用new关键字呢? 谢谢你的阅读,本

我怀疑你的问题是关于std :: vector <T>(而不是数组T [])。

  1. 当您的应用程序因任何原因崩溃或中止时,操作系统会回收内存。 如果不是,您使用的是真正罕见的操作系统,并发现了一个错误。
  2. 您需要区分向量本身使用的内存和其包含的对象的内存。 正如您所指出的那样,可以在堆上或堆栈上创建向量,它为其包含的元素分配的内存始终在堆上(除非您提供自己的分配器来执行其他操作)。 向量分配的内存由向量的实现管理,如果向量被破坏(因为它超出了堆栈上的向量的范围,或者因为你删除了堆上的向量),它的析构函数确保所有内存被释放。

不要使用new来创建向量。 把它们放在堆栈上。

向量的析构函数自动调用向量中每个元素的析构函数。 因此您不必担心自己删除对象。 但是,如果您有一个指针向量,则指针所引用的对象将不会被清除。 这是一些示例代码。 为清楚起见,我遗漏了大部分细节:

class HeapInt
{
    public:
        HeapInt(int i) {ptr = new int(i);}
        ~HeapInt() {delete ptr;}
        int& get() {return *ptr;}
    private:
        int* ptr;
};

int main()
{
    // this code DOES NOT leak memory
    std::vector<HeapInt> vec;
    for (int i = 0; i < 10; ++i)
    {
       HeapInt h(i);
       vec.push_back(h);
    }
    return 0;
}

即使main()抛出异常,也不会丢失内存。 但是,此代码确实泄漏了内存:

int main()
{
    // this code though, DOES leak memory
    std::vector<int*> vec;
    for (int i = 0; i < 10; ++i)
    {
       int* ptr = new int(i);
       vec.push_back(ptr);
    }
    // memory leak: we manually invoked new but did not manually invoke delete
    return 0;
}

是的,你可以相信矢量自己清理。

然而,你不能相信矢量持有清理后的东西。 需要清理的内容可能会在您的应用程序之外持续存在。 如果它的记忆,这不是一个担心。 如果它确保XML标签全部关闭,那么操作系统将无法帮助您。

例如,如果你有一个像这样的一个不稳定的锁对象的向量怎么办:

  class CLock
  {
  public:
      CLock() {}
      ~CLock() {}

      void Lock(...) {...}

      void Unlock(...) {...}
  };

  std::vector<CLock> myLockVec;

你的CLock的矢量如何在完成时解锁所有内容? Vector不是为了解锁而构建的。

这与具有指针向量的情况基本相同:

 std::vector<int*> myIntVec;

向量如何知道这里的哪些指针已被删除并且为NULL,哪些指针真的存在? 也许有些已被删除并设置为您的特殊值0xdeadbeef,意味着已删除。

关键是向量无法知道这个或知道它的元素是指针或锁或其他什么。 它们只需要具有默认构造函数且可复制的东西,并满足vector对其元素的其他此类要求。

解决方案是确保任何向量HOLDS都需要负责其清理。 这称为RAII - 资源分配是初始化,更重要的是,资源销毁是解除分配。 通过上面我们的CLock示例,答案是显而易见的,确保在我们完成后解锁!

 class CLock
 {  
      ...
      ~Clock()
      {
          if (locked)
          {
              Unlock();
          }
      }
 } 

但是指针并不那么明显。 解决方案是将指针包装在smart_ptr类中。 其中最多产的是聪明的poniters助推器系列

class CSmartPointer<T>
{
      CSmartPointer( T* rawPtr)
      {
         m_ptr = rawPtr;
      }

      ~CSmartPointer()
      {
         delete m_ptr;
      }
}

附加功能通过引用计数等指针发挥作用,但上面的示例应该为您提供问题性质的要点以及它通常如何解决。

程序创建的任何内存将在退出时释放。 这是操作系统的一个功能,与您正在使用的编程语言无关。

“每次我运行我的程序时,我都会松开RAM”必定是由于其他一些影响 - 你是如何测量的?

至于为什么要使用“新” - 有两个原因:

  • 您希望控制它们何时被释放
  • 您希望它们在当前函数退出后保持不变。

我想你谈的是std :: vector而不是语言数组。

  1. 当程序崩溃时,操作系统会恢复其内存
  2. std :: vector释放它分配的内存。 如果要存储指针,则不会删除它们。
  3. 向量是作为任何其他变量创建的,它们不在堆中,因为它们是向量。

我们两个中的一个在这里有点困惑。

如果使用std :: vector,则无需为其元素手动分配内存。 每当您执行push_back()时,将在需要时自动分配额外空间。 如果由于某种原因需要预先分配所有空间,可以调用reserve()。 无论哪种方式,当向量被破坏时,内存会自动为您释放。

如果你正在做新的std :: vector,你将获得一个指向向量的指针。 这与在任何其他类上调用new没有什么不同。 您创建一个指向该类对象的指针,当您调用delete时它将被破坏。 如果您不喜欢这种行为,请尝试在堆栈上创建向量。

对于“失去记忆”,@ RichieHindie说。

对于第二个问题:

可以将没有NEW关键字创建的向量留给他们自己的设备,并且即使代码在流程中被中止,也可以信任他们自己清理

虽然正常的程序终止(包括异常终止)确保析构函数执行(对于静态数据的那些有一些狡辩 - 理论上那些也应该运行,实际上你可能偶尔会遇到问题),这个过程的足够严重的崩溃不能保证任何行为 - 例如, kill -9 保证尽快终止你的程序,而不给它机会执行任何析构函数或其他任何东西。

在某些情况下,当vector是类的成员变量时,未提及的另一个场景是关于何时使用“new”。 NULL可用作附加信号量,例如在按需创建期间; 另外,如果向量稀疏地填充了类,那么除非真正需要它,否则甚至不创建一个将节省内存,代价是对所有实例额外的4字节惩罚以及指针间接的运行时惩罚。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM