Векторы внутри классов: обработка конструктора копирования и деструктора (С++)

Возьмем простой класс с "большим 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(...) 
   }
}

Я планирую сделать вектор планет, поэтому нужно "большое 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.

Как правильно удалить весовые и радиус-векторы, чтобы избежать утечек памяти (мне даже нужно)?

Ответ 1

Нет, вам не нужно ничего делать, потому что вы не управляете никакими ресурсами. Вы только пишете "Большая тройка", когда управляете ресурсом, но vector делает это. Это то, что написано в Большой тройке, вы просто используете его.

Вот почему принцип единой ответственности является ключевым в управлении ресурсами: как только у вас есть класс, который правильно управляет ресурсом, вы можете просто использовать его, даже не беспокоясь об этом ресурсе. Всегда разделяйте управление ресурсами из использования ресурсов.

Причина, по которой вам нужна Большая тройка, написанная в управляющем классе, состоит в том, что специальные члены по умолчанию обычно делают неправильную вещь (они копируют, назначают, уничтожают значения, а не то, что значения управляют/указывают на.) Но как только вы ресурс завернут (как в std::vector), все в порядке. По умолчанию будет скопирован вектор, но это копирование будет правильно записано.

Кстати, Большая тройка в контексте управления ресурсами (копирование и уничтожение ресурсов), а не их создание. Таким образом, это будет copy-constructor, copy-assign и destructor, а не конструктор по умолчанию.


Для информации, как вы это сделаете:

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
    }
};

Но не забудьте оператор копирования-присваивания. Я рекомендую идиому копирования и смены и оставляю это как упражнение для вас.

(Помните, что вам это действительно не нужно.)

Ответ 2

Вам не нужно реализовывать деструктор для вашего класса. Векторы будут автоматически уничтожены. Это связано с шаблоном "Инициализация ресурсов" .

Ответ 3

"Большая тройка" - это не то, что вы говорите. Это: конструктор копирования, оператор назначения копии и деструктор.

vector экземпляры уже скопированы и присваиваются, вам не нужно делать ничего особенного, поэтому с двумя членами типа vector<double> вам не нужно предоставлять пользовательские реализации больших трех.

Ваш конструктор копирования неверен, он не копирует исходные векторы в новый класс, он просто строит из них локальные функции, которые затем отбрасываются. Это создает локальные переменные с именем mass и radius, которые маскируют переменные-члены с тем же именем.

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. Поскольку у вас есть прямые переменные-члены, никаких специальных действий не требуется.

Ответ 4

Ваш класс также может быть упрощен:

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 знает, как правильно выполнять все три операции, и поэтому вам не нужно добавлять дополнительный код.

Ответ 5

Нет - вам не обязательно. дескрипторы переменных-членов автоматически вызывается после деструктора содержащего объекта.

Ответ 6

В вашей конкретной ситуации вам не обязательно! Вы не храните указатели в векторе. Вы не храните указатели на векторы в вашем классе планет (то есть вы не динамически выделяли объект vector < > , поэтому, чтобы попытаться удалить его).

Ответ 7

Как вы не new ed mass, вам не нужно его удалять.

Кроме того, деструкторы mass и radius будут вызываться автоматически, вам не нужно их явно называть

Ответ 8

  ~planets(){ 
     mass.clear();
     radius.clear();
  } 

выше должно быть достаточно, поскольку ваши векторные векторные объекты не имеют указателей.