使用类析构函数删除动态数组

Deleting dynamic array using class destructor

本文关键字:动态 数组 删除 析构函数      更新时间:2023-10-16

我有一个简单的程序,使用两个类存储3个变量的集合,一个叫做Stock,它有3个变量定义,以及基本的检查器和mutator函数,第二个叫做StockManagement的函数,它有一个指向Stock的指针,还有一些其他的函数,它们做各种各样的事情,比如创建一个动态的Stock数组,把文件数据转储到我们刚刚制作的数组中,等等。

在创建内容(例如构造函数、数组、输入数据、读取数据)方面一切都很好,但是当涉及到析构函数时,有时程序可能会在删除命令处出现错误,而当它没有出现错误时,由于某种原因,数据仍然是可访问的。

以下是我认为代码中重要的部分

头文件

class Stock {
private:
    char tag[5];
    int cost;
    long volume;
public:
    void setTag(char);
    void setCost(int);
    void setVolume(long);
    char* getTag();
    int getCost();
    long getVolume();
}
class StockManagement {
private:
    Stock* data;
    int size;
public:
    StockManagement();
    ~StockManagement();
   // other functions
}

Cpp文件(我忘记了正确的名称)

...
void Stock::setTag(char tag) {
    this->tag = tag;
}
void Stock::setCost(int cost) {
    this->cost = cost;
}
void Stock::setVolume(long volume) {
    this->volume = volume;
}
char* Stock::getTag() {
    return this->tag;
}
int Stock::getCost() {
    return this->cost;
}
long Stock::getVolume() {
    return this->volume;
}
// --
StockManagement::StockManagement() {
    data = NULL;
    size = 0;
}
StockManagement::~StockManagement() {
    if (data != NULL) {
        std::cout << data[0].getTag() << std::endl; // Debug line
        delete [] data;
        std::cout << data[0].getTag() << std::endl; // Debug line
        data = NULL;
        size = 0;
    }
}
void StockManagement::allocateMemory() {
    data = new Stock[size];
}
...

所以在调用析构函数的时间点上,那里总是有数据。有一个函数总是被调用,从文件中获取行数(并将其保存为size),用于分配内存,然后将数据输入到数组中。

然后就到了使用析构函数的时候了。第一行将一如预期地输出。从那时起,有可能发生两件事中的任何一件。我们在delete行设置错误,或者遍历它,调用第二个cout并以某种方式打印与第一次打印相同的数据。

显然数据没有被删除。但是为什么呢?为什么这种情况只会偶尔发生而其他时候只是单纯的隔离故障呢?

你的构造函数和析构函数都有这个

StockManagement::StockManagement() {
    data = NULL;
    size = 0;
}
StockManagement::~StockManagement() {
    if (data != NULL) {
        std::cout << data[0].getTag() << endl; // Debug line
        delete [] data;
        std::cout << data[0].getTag() << endl; // Debug line
        data = NULL;
        size = 0;
    }
}

你的构造函数没有问题,但作为个人偏好,我更喜欢这个:

StockManagement::StockManager() :
  data( nullptr ),
  size( 0 )
{}

对于你的析构函数,我在代码

中看到一个问题
StockManagement::~StockManagement() {
    if ( data != NULL ) {
        std::cout << data[0].getTag() << endl; // Debug Line
        delete[] data;
        std::cout << data[0].getTag() << endl; // Debug Line
        data = NULL;
        size = 0;
    }
 }

我看到了一些错误,可以优化以使阅读更容易。您的第一个错误是您正在使用std::cout,所以我假设您没有定义using namespace std;。这意味着您还需要对endl使用范围解析操作符;你需要将它声明为…std::endl;

您的第二个错误是在您调用delete [] data;之后,然后在下一行尝试访问数据索引0处的元素并调用getTag()方法。这是未定义的行为,可能导致崩溃,未处理的异常等。

至于可读性,类成员中的命名约定需要做一些修改。还有一件事要考虑的是,对于类中的任何指针成员,您可以根据需要使用std::unique_ptr<ClassType>std::shared_ptr<ClassType>。如果这个类拥有对象的唯一所有权,并且你不希望任何外部对象修改它,那么使用unique,但是如果你需要其他对象来处理这个数据成员,那么修改类保存修改后的数据,你可以使用shared。独特的美& &;shared是当对象超出作用域时,您不必担心在该对象上调用deletedelete[],并且您正在防止可能的内存泄漏。如果您的编译器支持,我也会用nullptr替换NULL的任何原始指针,但大多数(如果不是所有的话)现代编译器现在应该已经支持它了。如果您计划使用newdelete | delete []操作符,而不是在类声明中使用此操作符。
private:
    Stock* data;

我建议这样做

private:
    std::shared_ptr<Stock> data;
    // Or
    std::unique_ptr<Stock> data;

我希望这对你有帮助。

编辑:

我还注意到,在你的StockManagement类中,你定义了一个指向Stock对象的指针,这是一个类对象,然后你在构造函数中将其初始化为NULLnullptr,但在你的析构函数中,你在data上调用delete []。通常delete []用于指针数组,我没有看到在你的代码中定义。您确实有一个成员函数,使用括号操作符new来分配内存,但是您只将成员变量定义为指向对象的指针,而不是指向对象指针的指针。