【问题标题】:How do I delete a dynamically allocated array that is initialized in another function?如何删除在另一个函数中初始化的动态分配数组?
【发布时间】:2020-03-09 13:16:26
【问题描述】:
int* generateArray(int size)

这个函数应该动态创建一个数组,并将创建的数组返回给主程序中生成的数组。

int main() 

{

    int *numList = generateArray(501);
    cout << "Mode = " << findMode(arr, 501) << endl;

    cout << "Median = " << findMedian(arr, 501);

    delete[] numList;
    numList = nullptr;

    return 0;
}

我还需要删除动态分配的数组。我想确定我是否正确删除了新指针。通过删除 int main 末尾生成的,是否也会删除函数中的新指针?

int *generateArray(int size) 

{

    srand(time(0));

    int *arr = new int[size];

    for (int i=0; i<size; i++) 
    {
        arr[i] = rand() % 91 + 10;
    }
    return arr;
}

【问题讨论】:

  • 简短回答 - 是的。
  • 为什么不使用智能指针?还是std::arraystd::vector?手动内存管理,除了实现容器之类的东西时,通常是 2019 年/现代 C++ 中的代码异味。
  • 更长的答案:在现代 C++ 中避免使用原始的 newdelete
  • 注意:不是srand/rand,而是much better random number facilities,你应该完全在这些天使用。
  • @JesperJuhl 令我沮丧的是,一些老师对&lt;random&gt; 标头和其他现代 C++ 功能之类的东西不了解。在我的大学里,我们被教导使用来自&lt;cstdlib&gt;rand() 函数。当我自己找到&lt;random&gt; 标头时,我告诉了我的教授,他甚至都不知道它的存在。

标签: c++ arrays c++11


【解决方案1】:

我会使用生成函数内部的静态变量来执行此操作,该变量返回指向数组的指针。然后,在你的 delete 函数中,只需调用 generate 函数给你指针并删除它。要删除在 HEAP 上创建的数组,请使用 delete[] 运算符。应该调用此运算符来释放分配给new Type[] 的内存,其中Type 是任何类型(例如int)。删除(也称为解除分配)此内存后,将指向数组开头的指针设置为nullptr 几乎总是一个好主意。这样,您就不会意外地再次使用该指针。

这里有一些代码来说明我的意思:

int* getArray(unsigned long long elements_num = 0)
{
    static int* arr = nullptr;

    if (arr == nullptr && elements_num > 0)
    {
        arr = new int[elements_num];
        std::cout 
            << "Address of array being created: " << arr 
            << std::endl;
    }

    return arr;
}

void deleteArray()
{
    int* arrayAddress = getArray();

    if (arrayAddress == nullptr)
    {
        std::cerr << "Array not yet created" << std::endl;
    }
    else
    {
        std::cout 
            << "Address of array being deleted: " << arrayAddress 
            << std::endl;

        delete[] arrayAddress;
        arrayAddress = nullptr;
    }
}

int main()
{
    constexpr unsigned long long ARR_SIZE = 5;

    std::cout
        << "Trying to delete before creating array..."
        << std::endl;
    deleteArray();

    std::cout << '\n'
        << "Creating array with 5 elements..."
        << std::endl;
    int* myArray = getArray(ARR_SIZE);

    std::cout << '\n'
        << "Setting the values of the elements..."
        << std::endl;
    for (unsigned long long i = 0; i < ARR_SIZE; i++)
    {
        myArray[i] = static_cast<int>(i) + 1;
    }
    std::cout << "Values: ";
    for (unsigned long long i = 0; i < ARR_SIZE; i++)
    {
        std::cout << myArray[i] << ", ";
    }

    std::cout << "\n\n"
        << "Deleting array..."
        << std::endl;
    deleteArray();
    deleteArray(); // Trying to delete twice... Our program should be fine, right?
}

这是我运行它得到的输出:

Trying to delete before creating array...
Array not yet created

Creating array with 5 elements...
Address of array being created: 01147438

Setting the values of the elements...
Values: 1, 2, 3, 4, 5,

Deleting array...
Address of array being deleted: 01147438
Address of array being deleted: 01147438
[Program crashes]



编辑 1
然而,不要这么快就无条件接受。还有一个大问题。如果再次调用deleteArray(),它会再次尝试删除内存。为什么是这样?我们不是将数组设置回nullptr 吗?嗯……是的,不是的。

要理解,想想指针是什么;它只是一个变量。而且,就像任何其他变量类型一样,指针占用内存。这就像有一本书在你的办公桌上占据了很大的位置,但在冰箱上贴了一张便条,告诉你你的书在哪里。在这种情况下,note 是一个指针,而 book 是一个非指针变量。

现在,假设您在浴室里有另一张纸条,告诉您冰箱上的纸条在哪里。第二个音符就像一个双指针。双指针只是存储普通指针的内存位置。

回到问题。当我们调用getArray 时,我们所做的是我们创建了第二个指针变量(它有它自己的内存地址)。因此,当我们将第二个指针设置为nullptr 时,这并不意味着我们也将原始指针设置为nullptr。这就像冰箱上的便条和客厅里的便条都告诉您书在哪里。如果您擦除了客厅便条上的内容,这并不意味着您也擦除了冰箱上便条上的内容。

那么我们该如何解决这个问题呢?我们必须使用双指针。使用双指针意味着,当我们取消引用该指针一次时,我们会得到 原始 数组的实际内存地址。

问题是,一直弄乱双指针看起来是相当可怕的,所以我们可以制作一个让事情变得更容易忍受的函数,并且只在我们需要将原始数组设置为 @ 时才使用双指针987654333@。

这是我们的带有双指针的新代码:

// Notice the change in return type here
int** getArrayAddress(unsigned long long elements_num = 0)
{
    static int* arr = nullptr;

    if (arr == nullptr && elements_num > 0)
    {
        arr = new int[elements_num];
        std::cout 
            << "Address of array being created: " << arr 
            << std::endl;
    }

    // Notice the return went from "arr" to "&arr"
    return &arr;
}

void deleteArray()
{
    // Notice the change in variable type here
    int** arrayAddress = getArrayAddress();

    if (*arrayAddress == nullptr)
    {
        std::cerr << "Array not yet created" << std::endl;
    }
    else
    {
        std::cout 
            << "Address of array being deleted: " << *arrayAddress 
            << std::endl;

        // Notice we have to dereference once before deleting
        delete[] *arrayAddress;
        *arrayAddress = nullptr;
    }
}

// This is our convenience function so we don't have to mess with
// double pointers all the time
int* getArray(unsigned long long elements_num = 0)
{
    return *getArrayAddress(elements_num);
}

int main()
{
    constexpr unsigned long long ARR_SIZE = 5;

    std::cout
        << "Trying to delete before creating array..."
        << std::endl;
    deleteArray();

    std::cout << '\n'
        << "Creating array with 5 elements..."
        << std::endl;
    int* myArray = getArray(ARR_SIZE);

    std::cout << '\n'
        << "Setting the values of the elements..."
        << std::endl;
    for (unsigned long long i = 0; i < ARR_SIZE; i++)
    {
        myArray[i] = static_cast<int>(i) + 1;
    }
    std::cout << "Values: ";
    for (unsigned long long i = 0; i < ARR_SIZE; i++)
    {
        std::cout << myArray[i] << ", ";
    }

    std::cout << "\n\n"
        << "Deleting array..."
        << std::endl;
    deleteArray();
    deleteArray(); // Now the program really can handle this
}

这是它的输出:

Trying to delete before creating array...
Array not yet created

Creating array with 5 elements...
Address of array being created: 00C573A0

Setting the values of the elements...
Values: 1, 2, 3, 4, 5,

Deleting array...
Address of array being deleted: 00C573A0
Array not yet created

【讨论】:

    【解决方案2】:

    如何删除在另一个函数中初始化的动态分配数组?

    理想情况下,您返回一个拥有该数组的 RAII 容器,并在其自己的析构函数中处理该数组的销毁。如std::vectorstd::unique_ptr&lt;T[]&gt;


    如果 RAII 不是一个选项,例如跨语言 API,则在需要分配时,约定为资源的创建和销毁提供命名函数:

    int* generateArray(int size); // maybe calls new[]
    void destroyArray(int*);      // maybe calls delete[]
    

    这允许 API 的用户不依赖于分配细节。


    通过删除 int main 末尾生成的指针是否也会删除函数中的新指针?

    此时函数已经返回。该函数的所有局部变量都已销毁。 “函数中的指针”不再存在。 main 函数中的指针是该指针的副本:它具有相同的值。

    删除一个指针会破坏指向的对象(或数组),并释放内存。如果存在对该对象(或数组)的任何其他指针或引用,则这些其他指针将变为无效。这些其他指针不需要被删除,事实上,尝试这样做会导致未定义的行为。

    【讨论】:

    • 这是正确的做法,但它不会教 OP 所有内存操作如何在后台工作。
    • @0x400921FB54442D18 没有必要教他们错误的东西来学习。例如,教授如何实现(简化的)唯一指针或(非常简化的)向量。
    • 大多数机构对内存操作的“教学”无非是展示如何使用意大利面条式的new[] / delete[] 逻辑编写代码,类似于OP的问题。
    • 我被要求解决这个问题,删除 numList 会释放分配的空间吗?
    • @DajzkReaper 见最后一段。
    猜你喜欢
    • 1970-01-01
    • 2017-01-18
    • 2021-07-16
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多