Векторы внутри классов: обработка конструктора и деструктора копирования (C ++) - PullRequest
12 голосов
/ 18 августа 2010

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

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

Ответы [ 8 ]

23 голосов
/ 18 августа 2010

Нет, вам не нужно ничего делать, потому что вы не управляете никакими ресурсами. Вы пишете «большую тройку» только тогда, когда управляете ресурсом, но 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
    }
};

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

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

4 голосов
/ 18 августа 2010

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

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 голосов
/ 18 августа 2010

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

2 голосов
/ 18 августа 2010

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

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

1 голос
/ 18 августа 2010

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

0 голосов
/ 18 августа 2010

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

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

0 голосов
/ 18 августа 2010

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

0 голосов
/ 18 августа 2010
  ~planets(){ 
     mass.clear();
     radius.clear();
  } 

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

...