(Код 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;
}
Это правильный способ сделать что-то?
Нет, не в современном 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++.
В коде вы не вызываете ArrayQueue::ArrayQueue(int n)
, вместо этого вы вызываете конструктор по умолчанию ArrayQueue::ArrayQueue()
в котором вы не выделяете память для elem
.
std::unique_ptr
но никаких накладных расходов, что за сделка). Если подумать, всегда используйте умные указатели, что бы вы ни изменили.