【问题标题】:Do I have to delete this object? (if I don't intend to be ever deleted)我必须删除这个对象吗? (如果我不打算被删除)
【发布时间】:2013-10-07 22:56:25
【问题描述】:

我编写了一个小软件,想在堆上创建一个新对象。因此在类成员函数中我有

void  gShop::CreateCustomer(int type, int number)
    {
    vSlot[number] = new gCustopmer(type);
    vSlot[number]->itsContactToShop=itsShopNumber;
    vSlot[number]->itsNumber=number;
    }

其中 vSlot 是指向客户对象的指针向量。我有一个(这里:缩写)类 gShop,本质上是:

class gShop : public gBranch
   {
   public:
       gShop(): vSlot(100){}
      ~gShop(){}   

       std::vector <gCustomer*>   vSlot;
       ...
   }

我主要调用成员函数来创建新客户..

  vShop[0].CreateCustomer(TYPE_M20, 1);
  vShop[0].CreateCustomer(TYPE_F40, **2**);//EDIT:typo previously here. I intend to create customers by reading a file later on.

  std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
  std::cout<< "2" << vShop[0].vSlot[2]->itsTypeString << std::endl;

我知道我在“堆”上用“新”创建了两个对象(如果我正确处理术语 - 抱歉,我对没有受过正规教育的编程很陌生)并且我还有两个指向该对象的指针存储在object shop[0] 中的一个向量。

我的问题是,我听说每一个新的都有一个删除。我必须在哪里删除这个对象?我实际上不打算删除程序中任何创建的商店或客户对象。

其次,就不会导致内存泄漏而言,这段代码到目前为止还可以吗?我有点担心我在成员函数类中创建了新对象,所以我应该尝试在 gShop 的析构函数中实现 delete 并将指针设置为 NULL - 在理论上我应该想要删除 shop[0] ?

非常感谢。

【问题讨论】:

    标签: c++ memory-leaks heap-memory


    【解决方案1】:

    按照您编写代码的方式,您应该扩展 gShop 的析构函数实现以迭代 vector&lt;&gt; vSlotdelete 每个元素。因为您必须以这种方式管理内存以防止内存泄漏,所以您还需要遵循Rule of Three。因此,您还需要在复制构建期间做一些事情(深拷贝),并且您必须为赋值运算符做一些事情(清理即将被复制的vector&lt;&gt;,以及做一个深拷贝)。

    您可以通过使用智能指针来避免这些问题,并允许您的对象使用默认析构函数、复制构造函数和赋值运算符。例如:

        std::vector<std::shared_ptr<gCustomer>> vSlot;
    

    当你在vSlot中创建一个元素时,你可以使用make_shared()

        vSlot[number] = std::make_shared<gCustopmer>(type);
    

    当没有更多对内存的引用时,智能指针将为您删除内存。如果您没有可用的 C++.11,则可以改用 boost::shared_ptr

    智能指针将使您的gShop 的副本与它复制的原始gShop 共享相同的指针。智能指针使这种情况很好,因为在同一内存上不会有多个delete 调用。但是,如果您需要深拷贝语义,那么您仍然需要实现自己的拷贝构造函数和赋值运算符来制作深拷贝。

    如果您想要像智能指针一样自动清理的东西,但仍然使用默认复制构造函数和默认赋值运算符为您提供深层副本,那么您可以尝试使用boost::optional

    如果您使用的是g++ 4.4 或更高版本,那么您应该能够使用-std=gnu++0x-std=c++0x 启用C++.11 功能(如果您不想要GNU 扩展)。如果您有g++ 4.7 或更高版本,则选项为-std=gnu++11-std=c++11

    【讨论】:

    • 您应该将&gt;&gt; 修复为&gt; &gt;。此外,在提及深度复制方面做得很好。
    • @Cramer 没有什么可修复的。 std::shared_ptr 是 C++11,所以在没有空格的情况下连续关闭多个模板 &gt;&gt; 非常好。
    • 谢谢,我听说过 boost,使用它的许多功能会很可爱——尤其是矢量元素的快速迭代——但我有一个旧版本的代码块,没有它。我不知道如何安装它,当我开始升级到较新版本时,我的程序不再运行(我有很多 OPENGL),错误似乎通常无法解决:/ 我认为它很好锻炼吧。
    • @syam 谢谢,我没有意识到现在允许这样做。很高兴知道!
    • @user2856452:我更新了我的答案,你需要做些什么才能使用一些 C++.11 功能,即使是使用较旧的编译器。您接受的答案并没有警告您在类中处理裸指针的危险。可以接受这个答案(我的答案也涵盖了这种可能性),但也请牢记三法则(链接也在我的答案中)。
    【解决方案2】:

    每次使用new 创建对象时,都会从堆中取出一块内存。与此内存通信的唯一方法是通过您从new 获得的指针。当您在该指针上使用delete 时,内存将被释放并可用于其他目的。因此,当您丢失指针时,您会造成内存泄漏。
    在您的代码中,正确的方法是:
    从带有空指针的向量开始。 当您在精确位置创建对象时检查指针。如果它不是 null 你已经有对象并且必须删除它(或者可能抛出错误)

    void  gShop::CreateCustomer(int type, int number)
    {
        if(vSlot[number] != 0) {
             delete vSlot[number];
             vSlot[number] = 0;
        }
        vSlot[number] = new gCustopmer(type);
        vSlot[number]->itsContactToShop=itsShopNumber;
        vSlot[number]->itsNumber=number;
    }
    

    当向量被销毁时,您需要释放其中的所有内存。所以你的析构函数会是这样的:

    gShop::~gShop() {
        for(int i = 0; i < (int)vSlot.size(); ++i) {
              delete vSlot[i];
        }
    }
    

    【讨论】:

    • 这很酷。所以你的意思是像 vSlot(100, NULL) 而不是 vSlot(100)?然后我用 if 语句替换它,如果它是 NULL,如你所显示的那样?
    • 没有向量已经用空值初始化它。但是当你有原始指针时,你应该用 null 来初始化它们(这是一种很好的编写方式)。对不起,我在您创建功能时犯了错误(和正确答案)。你检查指针不是空对象是否已经存在。
    • CreateCustomer() 删除一个现有项目时,你应该将向量的那个槽设为NULL,以防随后的new 引发异常。
    • 顺便说一句。你知道我会在有 100 个标准构造函数客户的类中声明 vSlot 而不是 NULL 吗?
    • 有可能。如果您将对象存储在向量中,则不必担心内存管理。无需在开始时添加 100 个对象,通过 push_back 等添加它们。但无法存储交付的类。
    【解决方案3】:

    是的,必须释放从堆中分配的任何内存! 使用“新”,您将从 gCustomer 大小的堆中分配内存。 释放内存的好地方是在你的析构函数中:~gShop() 内存泄漏是由于没有释放内存造成的,尽管一旦关闭程序,所有内存都会自动释放。

    【讨论】:

      【解决方案4】:

      gShop 需要 delete 它创建的 gCustomer 对象。它可以在其析构函数中做到这一点,例如:

      class gShop : public gBranch
      {
      public:
          ...
          ~gShop()   
          ...
      };
      
      gShop::~gShop()
      {
          std::vector<gCustomer*>::iterator iter = vSlot.begin();
          std::vector<gCustomer*>::iterator end = vSlot.end();
          while (iter != end)
          {
              delete *iter;
              ++iter
          }
      }
      

      或者:

      void deleteCustomer(gCustomer *customer)
      {
          delete customer;
      }
      
      gShop::~gShop()
      {
          std::for_each(vSlot.begin(), vSlot.end(), deleteCustomer);
      }
      

      但是,您仍然会有内存泄漏。您将两个单独的gCustomer 对象存储在vShop[0] 的同一个vSlot[1] 插槽中,因此您丢失了您的一位客户的踪迹。我怀疑你打算这样做:

      vShop[0].CreateCustomer(TYPE_M20, 1);
      vShop[0].CreateCustomer(TYPE_F40, 2); // <-- was previously 1
      
      std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
      std::cout<< "2" << vShop[0].vSlot[2]->itsTypeString << std::endl;
      

      话虽如此,您应该重新考虑您的设计并让 STL 为您处理所有内存管理,例如:

      class gShop : public gBranch
      {
      public:
          std::vector <gCustomer> vSlot;
          ...
      };
      
      void gShop::CreateCustomer(int type)
      {
          vSlot.push_back(type);
          gCustomer &cust = vSlot.back();
          cust.itsContactToShop = itsShopNumber;
          cust.itsNumber = vSlot.size()-1;
      }
      

      vShop[0].CreateCustomer(TYPE_M20);
      vShop[0].CreateCustomer(TYPE_F40);
      
      
      // remember that vectors are 0-indexed
      std::cout<< "0" << vShop[0].vSlot[0].itsTypeString << std::endl;
      std::cout<< "1" << vShop[0].vSlot[1].itsTypeString << std::endl;
      

      或者:

      class gShop : public gBranch
      {
      public:
          std::vector <std::shared_ptr<gCustomer> > vSlot;
          ...
      };
      
      void gShop::CreateCustomer(int type)
      {
          std::shared_ptr customer = std::make_shared<gCustomer>(type);
          customer->itsContactToShop = itsShopNumber;
          customer->itsNumber = vSlot.size();
          vSlot.push_back(customer);
      }
      

      vShop[0].CreateCustomer(TYPE_M20);
      vShop[0].CreateCustomer(TYPE_F40);
      
      std::cout<< "0" << vShop[0].vSlot[0]->itsTypeString << std::endl;
      std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
      

      【讨论】:

      • 是的,应该是“2”——我打字时的错误。我实际上也不想删除 vSlot 的任何元素,因为我宁愿用“虚拟”客户填充它们。我没有得到shared_ptr?这是一种新的 C++ 语法吗?几年前,我使用旧版本和 OpenGL 启动了整个程序。它有几千行代码。我坚持使用我相信的那个版本,不能使用 shared_ptr。
      • shared_ptr 是 C++11 中的新功能。对于旧版本的 C++,您可以改用boost::shared_ptr
      • 我的收录甚至没有提升!我无法使代码与较新的版本一起使用。我没有遇到编译错误,但使用 OpenGL 时确实出现了令人讨厌的构建错误。当我研究它时,它被维持为未解决。所以我继续使用旧代码..
      猜你喜欢
      • 2017-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      相关资源
      最近更新 更多