为包含动态分配的数组成员c ++的类编写析构函数

时间:2014-11-12 20:37:58

标签: c++ destructor dynamic-memory-allocation

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

根据我能找到的内容(示例链接:How delete a pointer of classes which has pointer members?),这似乎是在这里定义析构函数的正确方法。但我尝试了这个,它遇到了运行时异常。 即使尝试过,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;
}

2 个答案:

答案 0 :(得分:3)

  

这是正确的做事方式吗?

不,不是现代C ++。例如,这里有一个问题:如果你按原样复制类A,你可以制作一个所谓的浅拷贝并只复制指针。如果现在其中一个对象超出范围,它将删除数组ptr,导致复制的类无法再访问它。

这就是为什么有“三个规则”的原因。它声明如果你需要一个析构函数,你很可能自己处理内存布局,因此也需要(至少)一个复制构造函数和赋值运算符。在您的情况下,这样的复制构造函数将进行“深层复制”并在复制的类中设置一个新数组,该数组包含与原始类中的数组相同的元素。

但是现代C ++中可以避免所有这些。


被认为是正确的方式就是使用管理其内存分配的成员。做你想做的最原生的方式是使用std::arraystd::vector而不是C风格的数组。

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 ++中非常重要。

答案 1 :(得分:1)

在您的代码中,您不会调用ArrayQueue::ArrayQueue(int n),而是调用默认构造函数ArrayQueue::ArrayQueue(),其中不为elem分配内存。