析构函数没有被调用

3
我已经写了以下代码来实现一个栈。
#include<iostream>
using namespace std;

class stack
{
    public:
        int *top;
        int  size, capacity;
    
    stack(int c):capacity(c){
        size = 0;
        top = new int[capacity];
        capacity = capacity - 1;
    }
    
    ~stack() // deleting the stack at the end of program
    {
        delete[] top;
        top = NULL;
        cout << "deleted the stack" << endl;
    }
    
    void push(int data)
    {
        // Is there is a space
        if(is_stackFull())
        {
            cout << "stack is full" << endl;
            return;
        }
        
        // populate the data
        *top   = data;
        
        // updating the stack variables
         top  += 1;
         size += 1;
        return;
    }
    
    void pop()
    {
        // is stack empty
        if(is_stackEmpty())
        {
            cout << "stack is empty" << endl;
            return; 
        }
        
        // update the stack variables
        top  -= 1;
        size -= 1;
        
        return;
    }
    
    bool is_stackFull()
    {
        return (size == capacity);
    }
    
    bool is_stackEmpty()
    {
        return (size == 0);
    }
};


int main()
{
    stack s(10);
    s.push(15);
    //s.pop(); // what happens to the output when i comment out this code?
    
    return 0;
}


在代码中,当我注释掉了s.pop()方法时,析构函数没有被调用。
有人可以解释这种行为背后的技术原因吗?
我尝试理解在C++类中如何调用析构函数。

1
push操作会增加top指针的值,使其不再指向你分配的内存的起始位置。试图删除它将导致未定义的行为。https://godbolt.org/z/7Phr5q3hv 你需要提出一个不同的方案,以确保你始终可以访问最初的分配内存。 - Retired Ninja
1
push操作会增加top指针的值,使其不再指向你分配的内存的起始位置。试图删除它将导致未定义的行为。https://godbolt.org/z/7Phr5q3hv 你需要想出一个不同的方案,以确保你始终可以访问最初的分配。 - Retired Ninja
3个回答

4
在`stack::push`中,您正在更改`top`指向的内容。因此,它不能再被删除。如果您push一次并且pop一次,`top`会恢复到原来的状态,错误就不会发生。
与其修改`top`指针,您可以跟踪一个顶部索引并对其进行修改。例如:
#include <iostream>
#include <algorithm>
#include <optional>

template <class T>
class Stack {
    public:
    using size_type = std::size_t;

    // Constructor
    Stack(size_type cap)
    : capacity(cap), top(0), data(new T[cap])
    { }

    // Destructor
    ~Stack() {
        delete[] data;
    }

    // Copy constructor
    Stack(const Stack& s)
    : capacity(s.capacity), top(s.top), data(new T[s.capacity])
    {
        std::copy(s.data, s.data+capacity, data);
    }

    // Copy assignment
    Stack& operator=(const Stack& s) {
        top = s.top;
        capacity = s.capacity;
        delete[] data;
        data = new T[capacity];
        std::copy(s.data, s.data+capacity, data);
        return *this;
    }

    // Move constructor
    Stack(Stack&& s)
    : capacity(s.capacity), top(s.top), data(s.data)
    {
        s.capacity = 0;
        s.top = 0;
        s.data = nullptr;
    }

    size_type get_capacity() const { return capacity; }
    size_type get_top() const { return top; }
    bool is_empty() const { return top == 0; }
    bool is_full() const { return top == capacity; }

    bool push(T val) {
        if (is_full()) return false;

        data[top++] = val;
        return true;
    }

    std::optional<T> pop() {
        if (is_empty()) return std::nullopt;

        return std::optional<T>{data[--top]};
    }

    private:
    T *data;
    size_type top;
    size_type capacity;
};

int main() {
    auto s = Stack<int>(10);

    s.push(19);
    s.push(67);

    std::cout << s.pop().value() << " "
              << s.pop().value() << std::endl;
}

嗨Chris,感谢你重新组织我的帖子并给出了合适的回复。 - siva ramaraju siv
嗨Chris,感谢你重新组织我的帖子并给出了合适的回复。 - siva ramaraju siv

3
析构函数确实被调用了,只是它没有按照你的期望执行。当你调用push而不调用pop时,top最终指向比起始位置右边一个空间。因此,析构函数最终在与new返回的地址不同的地址上调用delete,这属于未定义行为。这意味着你的代码基本上可以做任何想做的事情,在这种情况下显然是不打印deleted the stack消息。当我运行它时,我得到的结果是:
free(): invalid pointer
Aborted (core dumped)

要修复这个问题,你可以在调用delete之前,在析构函数中添加一行top -= size的代码,这样它就会指向相同的起始位置。

1
做这种数学运算似乎比简单地不改变data指针所指向的内容更加脆弱。 - Chris
1
做这个数学运算似乎比简单不改变 data 指针所指向的内容更为脆弱的解决方案。 - Chris
你可能是对的...我只是觉得指针算术更好地说明了涉及的概念,而没有添加其他干扰。如果目标是编写最清晰的程序,当然最好的选择就是使用std::stack - micaiah
你可能是对的...我只是觉得指针算术更好地说明了涉及的概念,而没有添加其他干扰。如果目标是编写最简洁的程序,当然最好的选择就是使用std::stack - micaiah

1
析构函数实际上被调用了,但它没有执行到cout << "deleted the stack" << endl;这一行。当你使用delete[] top时失败了。为什么呢?因为top不是一个有效的指针。相反,top指向的是在构造函数中分配的有效指针+4的位置。所以,假设在创建栈时top的值是0x004,在使用s.push(15)后,它变成了0x008(假设int类型占4个字节),当你尝试删除它时,你实际上尝试删除的是0x008,而这并不是一个有效的已分配内存地址,有效的地址应该是0x004。因此,在打印deleted the stack之前,程序就退出了,因为你试图释放一个无效的地址。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接