Написание деструктора для класса, содержащего динамически размещенный член массива c ++

0

(Код C++) Предположим, что у меня есть класс:

class A {
public: 
   int * ptr;
   int size;
    A();
    ~A();
}

A::A()
{
    ptr = new int[10];  // memory allocation is dependent upon user input into size variable 
}
A::~A()
{ 
    delete [] ptr; 
}

В соответствии с тем, что я мог найти (пример ссылки: Как удалить указатель классов, который содержит элементы указателя?), Это, по-видимому, правильный способ определения деструктора здесь. Но я пробовал это, и он запускается в исключение времени выполнения. Даже попытался delete ptr; но не повезло

Это правильный способ сделать что-то?

Реализация очереди на основе массива:

#include<iostream>
#include<string>
using namespace std;

class ArrayQueue
{
public:
    int front, size, rear, curr;
    int * elem;
    ArrayQueue():size(10),rear(-1),curr(0),front(-1){}
    ArrayQueue(int n);
    void enqueue(int& e);
    void dequeue();
    bool empty()const;
    int& getFront() const;
    int& getRear() const;
    int& getSize();
    ~ArrayQueue();

};

ArrayQueue::ArrayQueue(int n)
{
    size = n;
    front = -1;
    rear=-1;
    curr=0;
    elem = new int[size];
}
ArrayQueue::~ArrayQueue()
{  cout<<"running destructor";
    delete[] elem;
}

bool ArrayQueue::empty()const
{
    return  curr==0;
}
int& ArrayQueue::getSize()
{
    return curr;
}
int& ArrayQueue::getFront()const
{
    return elem[front];
}
int& ArrayQueue::getRear()const
{
    return elem[rear];
}
void ArrayQueue::enqueue(int& e)
{
    if(getSize()==size){cout<<"Queue full"<<'\n'; return;}
    rear = ((rear+1)%size);
    elem[rear]=e;
    ++curr;
}
void ArrayQueue::dequeue()
{
    if(empty()){cout<<"Queue empty"<<'\n';return;}
    front = ((front+1)%size);
    --curr;
}


int main()
{
    ArrayQueue object;
    cout<<"Size of the queue"<<object.size<<'\n';
    cout<<"Current size:"<<object.getSize()<<'\n';
    object.dequeue();
    for(int i =0 ; i<15; i++)
    {
        object.enqueue(i);
    }
    cout<<"front element : "<<object.front<<'\n';
    cout<<"rear element : "<<object.rear<<'\n';
    cout<<"Current size:"<<object.getSize()<<'\n';
    object.dequeue();
    object.dequeue();
    object.dequeue();
    cout<<"front element : "<<object.front<<'\n';
    cout<<"rear element : "<<object.rear<<'\n';
    return 0;
}
  • 1
    Ищите «Правило 3» (возможно, «Правило 5») и «Правило 0». У вас либо слишком мало переопределенных специальных функций-членов, либо слишком много (в последнем случае вы должны использовать умный указатель, такой как std::unique_ptr но никаких накладных расходов, что за сделка). Если подумать, всегда используйте умные указатели, что бы вы ни изменили.
  • 1
    Правило трех (пять C ++ 11)
Показать ещё 4 комментария
Теги:
destructor
dynamic-memory-allocation

2 ответа

3
Лучший ответ

Это правильный способ сделать что-то?

Нет, не в современном C++. Например, вот одна проблема: если вы скопируете класс A как есть, вы сделаете так называемую мелкую копию и скопируете только указатель. Если теперь один из этих объектов выходит за пределы области видимости, он удаляет массив ptr с тем эффектом, что скопированный класс больше не может к нему обращаться.

Вот почему есть что-то вроде "правила трех". В нем указано, что если вам нужен деструктор, вполне вероятно, что вы сами обрабатываете макет памяти, и поэтому также требуете (по крайней мере) конструктора копирования и оператора присваивания. В вашем случае такой конструктор копий сделает "глубокую копию" и настроит новый массив в скопированном классе, который содержит те же элементы, что и массив в исходном классе.

Но этого можно избежать в современных C++.


Способ, который считается правильным, заключается в том, чтобы использовать членов, которые сами управляют своим распределением памяти. Самый естественный способ сделать то, что вы намеревались, - использовать std::array или std::vector вместо массива C-style.

struct A
{
   //everything is handled correctly here without destructor, copy constructor, etc.
   //this is an example of RAII or the rule of zero 
   std::vector<int> v;
}

Копии, уничтожение и назначение теперь выполняются путем вызова соответствующего конструктора копирования, деструктора и оператора присваивания std::vector, который вы можете быть уверены в его правильности. Итак, если вы делаете копию своего класса, вы создаете два разных объекта std::vector.


Другой вариант - использовать std::shared_ptr.

struct A
{
   std::shared_ptr<std::vector<int> > v;   //you could also use an int* here,
                                           //but always prefer std::vector 
}

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

Если вы сделаете копию своего класса A, это снова мелкая копия, то есть вы не копируете объект, на который указывает shared_ptr, но только указатель. Однако теперь shared_ptr подсчитывает количество экземпляров, указывающих на управляемый std::vector и не позволяет скопированному классу удалять управляемый объект, если он выходит за рамки.


Какие из этих альтернатив вам понадобятся, зависит от ваших требований. В каждом случае вам следует ознакомиться с этими понятиями, поскольку, на мой взгляд, они очень важны в C++.

  • 0
    Большое спасибо, обязательно прочту.
1

В коде вы не вызываете ArrayQueue::ArrayQueue(int n), вместо этого вы вызываете конструктор по умолчанию ArrayQueue::ArrayQueue() в котором вы не выделяете память для elem.

  • 0
    Большое спасибо, я не замечаю эту глупую ошибку.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню