如何在c++中释放指向结构的指针数组

How to deallocate array of pointers to structures in C++?

本文关键字:结构 指针 数组 释放 c++      更新时间:2023-10-16

我创建了一个类,实现了指向结构的指针数组…我知道如何添加记录到这个数组,但我不知道,如何正确地删除它们,因此我有内存泄漏。我的数组的大小在需要时增加,我知道数组的大小以及我在那里有多少条记录。我习惯用有垃圾回收器的语言编码,所以这对我来说很困惑。如果你们中有人能告诉我,如何正确地释放这个数组,我将很高兴。

请注意,我不能使用vector。我的include限定为:

#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>

我的代码:

struct DbRecord
{
    string oName;
    string oAddr;
    string cName;
    string cAddr;
};
class CCompanyIndex
{
    public: CCompanyIndex(void);~CCompanyIndex(void);
    bool Add(const string & oName,
    const string & oAddr,
    const string & cName,
    const string & cAddr);
    bool Del(const string & oName,
    const string & oAddr);
    bool Search(const string & oName,
    const string & oAddr,
    string & cName,
    string & cAddr) const;
    int size;
    int position;
    DbRecord * * db;
};
CCompanyIndex::CCompanyIndex(void)
{
    db = new DbRecord * [1000];
    size = 1000;
    position = 0;
}
CCompanyIndex::~CCompanyIndex(void)
{
}
int main(int argc, char const * argv[])
{
    CCompanyIndex c1;
    // do something..with c1, i.e. add there some records to array
    // ...
    // ...
    // delete it now
}

经验法则是在析构函数中反转构造函数中与内存管理相关的操作。也就是说,既然在构造函数中有db = new DbRecord * [1000];,那么在析构函数中也应该有delete[] db;

请注意,这里很可能不需要动态内存管理(使用按值语义),您可能希望查看c++提供的更高级的抽象,例如由AndyProwl和JamesKanze建议的vector类。

使用std::vector来避免必须通过原始指针、new[]delete[]手动管理内存。这样做(正如您所经历的)容易出错,并且很容易导致内存泄漏或未定义行为。

出于同样的原因,我还建议使用智能指针而不是原始指针来保存对DbRecord对象的引用。您应该根据所需的所有权策略选择智能指针。在这里,我假设std::shared_ptr是合适的。但是,请注意,如果不需要引用语义,则根本不应该使用指针,而应该将容器声明为std::vector<DbRecord>。这里我假设您确实需要引用语义,因为您的原始版本使用(原始)指针;但是,如果不需要,请不要使用指针。

因此给定必要的#include指令和using声明:

#include <string>
#include <vector>
#include <memory>
using std::string;
using std::vector;
using std::shared_ptr;

和(未改变的)DbRecord数据结构的定义:

struct DbRecord {
    string oName;
    string oAddr;
    string cName;
    string cAddr;
};

您可以这样更改CCompanyIndex的定义(如您所见,用户定义的默认构造函数和析构函数现在是多余的,您可以让编译器隐式地生成它们):

class CCompanyIndex {
public:
//  No more need for a user-defined default constructor and destructor!
    bool Add(const string & oName,
            const string & oAddr,
            const string & cName,
            const string & cAddr);
    bool Del(const string & oName,
            const string & oAddr);
    bool Search(const string & oName,
            const string & oAddr,
            string & cName,
            string & cAddr) const;
    std::vector<std::shared_ptr<DbRecord>> db;
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
};

最后,注意您不需要执行任何手动清理:

int main(int argc, char const *argv[])
{
    CCompanyIndex c1;
    // do something..with c1, i.e. add there some records to array
    // ...
    // ...
    // NO NEED TO MANUALLY DELETE IT NOW!
}

您不应该在这里进行任何内存管理。有几乎没有任何理由使用array new;只使用std::vector。真的没有理由动态地分配DbRecord;因为它具有值语义:

std::vector<DbRecord> db;

很好地完成了这项工作。

您可以使用deletedelete[]操作符来释放内存。例如,可以像下面这样释放析构函数中的内存:

CCompanyIndex::~CCompanyIndex(void)
{
  // delete objects pointed-to in a loop. 
  // !! the objects MUST have been allocated, or the pointers MUST be set to null. 
  // note that 'delete' is null-safe. 
  for(DbRecord* it=db; it<db+position; ++it)
  {
    delete *it; 
  }
  // delete the array of pointers
  delete[] db;
}

delete释放为单个对象分配的内存(通过new),而delete[]释放为对象数组分配的内存(通过new[],如您的情况)。

但是不要重新发明轮子-只使用std::vector<DbRecord*>。它为您做内存管理,您的代码将更简单,更易读。c++提供了强大的标准库,因此大多数程序员甚至不需要使用手动内存管理。