【问题标题】:Vectors within classes: handling copy constructor and destructor (C++)类中的向量:处理复制构造函数和析构函数 (C++)
【发布时间】:2011-03-31 10:53:04
【问题描述】:

以“big 3”(构造函数、复制构造函数、析构函数)的简单类:

#include <vector>
using namespace std; //actually goes in the C file that links to this header file
...
class planets(){ //stores mass and radii data for planets in a solar system.
   public:
      vector <double> mass;
      vector <double> radius;

   //constructor
   planets( int numObj ){
     for(int i=0; i<numObj; i++){
         mass.push_back(8.0); //some default values.
         radius.push_back(2.0);
     }
   }
   //copy constructor
   planets(const planets &p){
      vector <double> mass(p.mass); //copy vectors into new class.
      vector <double> radius(p.radius);
   }
  //destructor
  ~planets(){
     delete mass; //ERROR: (...) argument given to ‘delete’, expected pointer
     ~radius(); //also causes error: no match for call to(...) 
   }
}

我计划制作一个行星矢量,因此需要“big 3”:

vector <planets> stars;
stars.push_back(planets(5)); //5 hypothetical planets of alpha centauri
stars.push_back(planets(8)); //our solar system. Used to be nine.
///etc.

如何正确删除质量和半径向量,以避免内存泄漏(我什至必须这样做)?

【问题讨论】:

  • 顺便说一句,您的复制构造函数(根本没有必要)没有做您认为它正在做的事情。
  • Nit:三巨头包括复制赋值运算符 (operator=),而不是普通 ctor。
  • @dirkgently:这不是傻瓜,这是一个基本点!
  • @dirkgently:这个名字总是让我感到困惑。这应该是四规则。 (好的:我知道你可以摆脱三个,因为通过定义复制构造函数,你现在不再需要定义默认构造函数,但这很少有用,除了其他三个之外,你还需要一个普通的构造函数。跨度>
  • @Martin York:老实说,我已经思考了很长时间了。我能得出的唯一“合理”的事后理由是:假设您将定义一个演员。关键是,如果您定义了 other 三个中的任何一个,您可能会需要所有这些。任何人都可以拥有一个 ctor,但并非所有人都需要明确定义其他三个中的任何一个。 YMMV。

标签: c++ class memory-management vector destructor


【解决方案1】:

您不必为您的类实现任何析构函数。向量将被自动销毁。这与Resource Acquisition Is Initialization 模式有关。

【讨论】:

    【解决方案2】:

    不-您不必这样做。成员变量的析构函数在包含对象的析构函数之后自动调用。

    【讨论】:

      【解决方案3】:

      不,您不需要做任何事情,因为您不管理任何资源。您只在管理资源时编写三巨头,但 vector 正在这样做。 它是三巨头写得正确的那个,你就用它吧。

      这就是为什么单一职责原则是资源管理中的关键:一旦你有某个类可以正确管理资源,你就可以简单地使用它,而不必再次担心该资源。 始终将资源管理与资源使用分开。

      您需要在管理类中编写三巨头的原因是因为默认的特殊成员通常会做错事(他们复制、分配、破坏值而不是值管理/指向的内容。)但是一旦你资源被打包(就像std::vector),一切都很好。默认会复制向量,但是复制是正确写入的。

      顺便说一句,三巨头是在管理资源(复制和销毁资源)的上下文中,而不是创建它们。所以它将是复制构造函数、复制赋值和析构函数,而不是默认构造函数。


      供您参考,以下是您的操作方法:

      class planets
      {
      public:
          // ...
      
          //copy constructor
          planets(const planets &p) : // use an initialization list to initialize
          mass(p.mass), // copy-construct mass with p.mass
          radius(p.radius) // copy-construct radius with p.radius
          {
              // what you had before just made a local variable, copy-constructed
              // it with p.xxx, then got released (nothing happened to your members)
          }
      
          //destructor
          ~planets()
          {
              // nothing to do, really, since vector destructs everything 
              // right for you, but you yes, you would delete any resources
              // you managed here
          }
      };
      

      但不要忘记复制赋值运算符。我推荐copy-and-swap idiom,并将其作为练习留给您。

      (请记住,您实际上并不需要这些。)

      【讨论】:

        【解决方案4】:
          ~planets(){ 
             mass.clear();
             radius.clear();
          } 
        

        以上内容应该足够了,因为您的成员向量对象没有任何指针。

        【讨论】:

        • 即使这不是必需的。 vector 无论如何都会释放其析构函数中的内存。
        • 我知道。我试图向 OPoster 展示在析构函数中正确的做事方式。删除质量和〜半径不正确。
        • @chubsdad:我想人们正在投票,因为clear不需要 必需的(正如@Naveen 所说),因为将调用向量析构函数。致电clear 表明对这一基本点缺乏了解。
        【解决方案5】:

        在您的特定情况下,您不必这样做!您没有在向量中存储指针。您没有在行星类中存储指向向量的指针(也就是说,您没有动态分配 vector 对象,所以为什么要尝试删除它)。

        【讨论】:

          【解决方案6】:

          因为你没有newedmass,所以你不需要删除它。

          另外,massradius 的析构函数会被自动调用,你不需要显式调用它们

          【讨论】:

            【解决方案7】:

            “三巨头”并不是你所说的那样。它们是:拷贝构造函数、拷贝赋值运算符和析构函数。

            vector 实例已经是可复制和可分配的,您不必做任何特别的事情,因此对于 vector&lt;double&gt; 类型的两个成员,您不需要提供三巨头的自定义实现。

            您的复制构造函数不正确,它不会将源向量复制到新类中,它只是从它们构造函数局部变量,然后将其丢弃。这会创建名为 massradius 的局部变量,它们会屏蔽同名的成员变量。

            planets(const planets &p){
              vector <double> mass(p.mass); //copy vectors into new class.
              vector <double> radius(p.radius);
            }
            

            更正确(但不必要)的是:

            planets(const planets &p)
              : mass(p.mass) //copy vectors into new class.
              , radius(p.radius)
            {
            }
            

            同样,你的析构函数体应该是空的。你只有delete 已经分配了new 的指针。由于您有直接的成员变量,因此不需要特殊操作。

            【讨论】:

            • 但更正确的是不使用编译器生成的默认版本。
            【解决方案8】:

            您的课程也可以简化:

            class planets()
            { //stores mass and radii data for planets in a solar system.
              public:
                std::vector<double> mass;
                std::vector<double> radius;
            
              //constructor
              planets( int numObj )
              {
                for(int i=0; i<numObj; i++)
                {
                  mass.push_back(8.0); //some default values.
                  radius.push_back(2.0);
                }
              }
            }
            

            您的类不包含任何资源(即指针)
            因此,您不需要显式管理它们。每个班级都应该知道如何:

            • 复制自身
            • 被分配到
            • 自毁

            编译器生成的复制构造函数赋值运算符和析构函数会自动在任何成员变量(质量和半径)上调用这些操作,因此您也不需要。因此,在您的情况下,std::vector 知道如何正确执行所有三个操作,因此您不需要添加任何额外的代码。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-05-27
              • 2013-04-17
              • 2011-04-03
              • 2010-12-16
              • 2016-09-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多