简体   繁体   English

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

[英]How do I delete a dynamically allocated array that is initialized in another function?

int* generateArray(int size)

This function should dynamically create an array and should return the created array to the array generated in the main program.这个 function 应该动态创建一个数组,并且应该将创建的数组返回给主程序中生成的数组。

int main() 

{

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

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

    delete[] numList;
    numList = nullptr;

    return 0;
}

I also need to delete the dynamically allocated array.我还需要删除动态分配的数组。 I wanna make sure if I deleted the new pointer properly.我想确定我是否正确删除了新指针。 By deleting the generated in the at the end of int main would it delete the new pointer in the function as well?通过删除 int main 末尾生成的,它是否也会删除 function 中的新指针?

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

How do I delete a dynamically allocated array that is initialized in another function?如何删除在另一个 function 中初始化的动态分配数组?

Ideally, you return a RAII container that owns the array, and takes care of the destruction of the array in their own destructor.理想情况下,您返回一个拥有该数组的 RAII 容器,并在其自己的析构函数中处理该数组的销毁。 Such as std::vector or std::unique_ptr<T[]> .例如std::vectorstd::unique_ptr<T[]>


In case RAII is not an option, such as a cross language API, when allocation is necessary it is a convention to provide named functions for both creation and destruction of the resource:如果 RAII 不是一个选项,例如跨语言 API,则在需要分配时,约定为资源的创建和销毁提供命名函数:

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

This allows the user of the API to not depend on the allocation details.这允许 API 的用户不依赖于分配细节。


By deleting the generated in the at the end of int main would it delete the new pointer in the function as well?通过删除 int main 末尾生成的,它是否也会删除 function 中的新指针?

The function has already returned by that point. function 到那时已经返回。 All local variables of that function have been destroyed.该 function 的所有局部变量已被销毁。 The "pointer in the function" no longer exists. “函数中的指针”不再存在。 The pointer in main function is a copy of that pointer: It has the same value.主 function 中的指针是该指针的副本:它具有相同的值。

Deleting one pointer destroys the pointed object (or array), and deallocates the memory.删除一个指针会破坏指向的 object(或数组),并释放 memory。 If there were any other pointers or references to that object (or array), those other pointers would become invalid.如果有任何其他指向该 object(或数组)的指针或引用,则这些其他指针将变为无效。 Those other pointers don't need to be deleted and in fact, attempting to do so would result in undefined behaviour.那些其他指针不需要被删除,事实上,尝试这样做会导致未定义的行为。

I would do it using a static variable inside of your generate function that returns the pointer to the array.我会在生成的 function 中使用 static 变量来执行此操作,该变量返回指向数组的指针。 Then, inside your delete function, simply call the generate function to give you the pointer and delete it.然后,在你的删除 function 中,只需调用生成 function 给你指针并删除它。 To delete an array created on the HEAP, use the delete[] operator.要删除在 HEAP 上创建的数组,请使用delete[]运算符。 This operator should be called to free memory allocated with new Type[] , where Type is any type (eg an int ).应该调用此运算符来释放分配有new Type[]的 memory ,其中Type是任何类型(例如int )。 After you delete (aka deallocate) this memory, it's almost always a good idea to set the pointer that was pointing to the start of the array to nullptr .删除(又名解除分配)此 memory 后,将指向数组开头的指针设置为nullptr几乎总是一个好主意。 That way, you don't accidentally use that pointer again.这样,您就不会意外地再次使用该指针。

Here's some code to show what I mean:这是一些代码来说明我的意思:

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?
}

Here's the output I got from running it:这是我从运行它得到的 output:

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]



Edit 1编辑 1
However , don't be so quick to accept this unconditionally.但是,不要这么快就无条件接受这一点。 There is still a big issue.还有一个大问题。 If you call deleteArray() again, it will try to delete the memory again.如果再次调用deleteArray() ,它会再次尝试删除 memory。 Why is this?为什么是这样? Didn't we set the array back to nullptr ?我们不是将数组设置回nullptr吗? Well... yes and no.嗯……是的,不是的。

To understand, think about what a pointer is;要理解,请考虑指针是什么; it's just a variable.它只是一个变量。 And, just like any other variable type, a pointer takes memory.而且,就像任何其他变量类型一样,指针采用 memory。 It's like having a book that takes up a big spot on your desk, but then having a note on the fridge telling you where your book is.这就像有一本书在你的办公桌上占据了很大的位置,但在冰箱上贴了一张便条,告诉你你的书在哪里。 The note, in this case, is a pointer, and the book is a non-pointer variable.在这种情况下,note 是一个指针,而 book 是一个非指针变量。

Now, imagine you had another note in the bathroom telling you where the note on the fridge is.现在,假设你在浴室里有另一张纸条,告诉你冰箱上的纸条在哪里。 This second note is like a double pointer.第二个音符就像一个双指针。 A double pointer simply stores the memory location of the normal pointer.双指针只存储普通指针的 memory 位置。

Back to the problem.回到问题。 What we did when we called getArray is we created a second pointer variable (that has it's own memory address).我们在调用getArray时所做的是创建了第二个指针变量(它有自己的 memory 地址)。 So, when we set that second pointer to nullptr , that doesn't mean we set the original pointer to nullptr too.因此,当我们将第二个指针设置为nullptr时,这并不意味着我们也将原始指针设置为nullptr It would be like having the note on your fridge plus a note in your living room both telling you where the book is.这就像冰箱上的便条和客厅里的便条都告诉您书在哪里。 If you erase what's on the note in the living room, that doesn't mean you also erased what's on the note on the fridge.如果你在客厅里擦掉了纸条上的东西,那并不意味着你也擦掉了冰箱上纸条上的东西。

So how do we fix this?那么我们如何解决这个问题呢? We have to use a double pointer.我们必须使用双指针。 Using a double pointer means, when we dereference that pointer one time, we get the actual memory address of the original array.使用双指针意味着,当我们取消引用该指针一次时,我们会得到原始数组的实际memory 地址。

The thing is, messing with double pointers all the time is rather horrible to look at, so we can make a function that makes things a little more bearable and only use the double pointer when we need to set the original array to nullptr .问题是,一直弄乱双指针看起来相当可怕,所以我们可以制作一个 function 让事情变得更容易忍受,并且只在我们需要将原始数组设置为nullptr时才使用双指针。

Here's our new code with double pointers:这是我们带有双指针的新代码:

// 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
}

And here's its output:这是它的 output:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM