如何释放动态结构数组的内存

How to free memory of dynamic struct array

本文关键字:结构 数组 内存 动态 何释放 释放      更新时间:2023-10-16

作为一个从来没有处理过释放内存等问题的人,我得到了创建一个动态结构数组和创建函数来添加或删除数组元素的任务。当删除时,我必须释放不再需要的内存。

在删除大小为3的数组的第二个元素时,我将第3个元素移动到第二个位置,然后删除最后一个元素。当删除最后一个,我总是得到一个错误…有人能帮我找到解决办法吗?

struct myFriend {
    myFriend() {
        number=0;
        hobbys = new char*[10];
    }
    int number;
    char* name;
    char** hobbys;
};
int main() {
    myFriend* friendList = new myFriend[10];
    myFriend* tempFriend = new myFriend;
    tempFriend->number=1;
    tempFriend->name = "ABC";
    myFriend* tempFriend2 = new myFriend;
    tempFriend2->number=2;
    tempFriend->name = "XYZ";
    myFriend* tempFriend3 = new myFriend;
    tempFriend3->number=3;
    tempFriend3->name = "123";
    friendList[0] = *tempFriend;
    friendList[1] = *tempFriend2;
    friendList[2] = *tempFriend3;
    friendList[1] = friendList[2]; //move 3rd element on 2nd position
    delete &(friendList[2]); //and delete 3rd element to free memory
}

为什么要创建临时变量?他们甚至不需要。

如果您使用std::vectorstd::string,您面临的问题将自动消失:

std::vector<myFriend> friendList(10);
friendList[0]->number=1;
friendList[0]->name = "ABC";
friendList[1]->number=2;
friendList[1]->name = "XYZ";
friendList[2]->number=3;
friendList[2]->name = "123";

要使其工作,您应该将结构重新定义为:

struct myFriend {
    int number;
    std::string name;
    std::vector<std::string> hobbys;
};

如果你被要求使用原始指针,那么你应该这样做:

struct Friend 
{
    int    number;
    char*  name;
};
Friend * friends = new Friend[3];
friends[0]->number=1;
friends[0]->name = new char[4];
strcpy(friends[0]->name, "ABC");
//similarly for other : friends[1] and friends[2]
//this is how you should be deleting the allocated memory.
delete [] friends[0]->name;
delete [] friends[1]->name;
delete [] friends[2]->name;
delete [] friends; //and finally this!

如果你做了以下任何一件事,它都是错误的,并且会调用未定义的行为:

delete friends[2];    //wrong
delete &(friends[2]); //wrong

不可能从new []分配的数组中删除子集

myFriend* friendList = new myFriend[10];

你有一个完整的数组

+------------------------------------------------------------------+
|  friendList[0]  |  friendList[1]  |    .....  |   friendList[9]  | 
+------------------------------------------------------------------+

你不能delete &(friendList[2])。你从C++得到整个10个元素的数组。这个数组从friendList(或&(friendList[0]))开始。

operator delete和指向new(即friendList)返回的地址的指针是有效的只有。

我注意到两件事。(1)你显然应该"创建函数来添加或删除元素",但你还没有这样做,你只创建了一个函数。(2)使用同样需要管理内存的结构体会使工作变得比需要的更加困难。我建议你使用一个更简单的结构体。

实际上,你的任务是创建一个简单的"向量"类,所以我建议你这样做。从一个空结构体开始。如果老师要求你按照写的那样使用myFriend结构体,你可以在之后添加,你完成了你的向量函数。我假设你现在还不允许上课,因为大多数老师都会把这个问题留到最后。

struct MyStruct {
    int value; // start with just one value here. Dealing with pointers is more advanced.
};
MyStruct* array;
int size;
int capacity;
void addMyStruct(MyStruct& value); // adds a MyStruct object to the end.
void removeMyStructAtPosition(int position); // removes the MyStruct object that is at 'position'
// I leave the functions for you to implement, it's your homework after all, but I give some clues below.
void addMyStruct(MyStruct& value) {
    // First check that there is enough capacity in your array to hold the new value. 
    // If not, then make a bigger array, and copy all the contents of the old array to the new one.
    // (The first time through, you will also have to create the array.)
    // Next assign the new value to array[size]; and increment size
}
void removeMyStructAtPosition(int position) {
    // If the position is at end (size - 1,) then simply decrement size.
    // Otherwise you have to push all the structs one to the left (array[i] = array[i + 1])
    // from position to the end of the array.
}
int main() {
    // test your new class here.
    // don't forget to delete or delete [] any memory that you newed.
}

数组大小固定为10,因此您不需要从中删除任何元素。但是您确实需要删除friendList[1]namehobbys元素(并且在覆盖它之前)。这里有两个问题:

  1. 您正在设置friendList[0]->name = "ABC";在这里,"ABC"是一个常量零终止字符串在内存的某个地方。您不允许删除它。所以你得复印一份。
  2. hobby[i]被分配时,您想要删除它。但在你的代码中,你无法判断它是否被分配了。因此,必须将构造函数中的每个元素设置为0,以便稍后知道要删除哪些元素。

删除这些元素的合适位置是在myFriends的析构函数中。

似乎这个问题的重点是管理一个动态数组。主要问题是他使用的是一个friendList数组。使用指向friendList的指针数组:

struct myFriend {
    myFriend() {
        number=0;
        hobbys = new char*[10];
    }
    int number;
    char* name;
    char** hobbys;
};
int main() {
    myFriend** friendList = new myFriend*[10];
    myFriend* tempFriend = new myFriend;
    tempFriend->number=1;
    tempFriend->name = "ABC";
    myFriend* tempFriend2 = new myFriend;
    tempFriend2->number=2;
    tempFriend->name = "XYZ";
    myFriend* tempFriend3 = new myFriend;
    tempFriend3->number=3;
    tempFriend3->name = "123";
    friendList[0] = tempFriend;
    friendList[1] = tempFriend2;
    friendList[2] = tempFriend3;
    friendList[1] = friendList[2]; //move 3rd element on 2nd position
    delete friendList[2]; //and delete 3rd element to free memory
}

但是其他人都是对的——关于'hobbys'和'name'的内存分配存在主要问题,您需要分别进行排序。

为了做好功课,我建议你学习更多关于指针、new/delete操作符、new[]/delete[]操作符(不要与new/delete操作符混淆)和对象创建/复制/构造函数/析构函数的知识。这是基本的c++特性,你的任务就是这些。

指引方向:

1)当你像这样动态分配对象时

MyType* p = new MyType;

MyType* p = new MyType(constructor_parameters);

您获得指向创建对象的指针p (newMyType类型的单个对象分配内存并调用该对象的构造函数)。

处理完对象后,必须调用

delete p;

delete调用对象的析构函数,然后释放内存。如果不调用delete,内存就会泄露。如果你多次调用它,行为是未定义的(可能堆损坏,可能导致程序崩溃-有时在非常奇怪的时刻)。

当你像这样动态分配数组
MyType* p = new MyType[n];

您获得指向n创建对象的数组的指针p顺序位于内存中(new[]MyType类型的n对象分配单个内存块,并为每个对象调用默认构造函数)。

你不能改变这个动态数组中元素的数量。你只能删除它。在处理完数组之后,必须调用

delete[] p;  // not "delete p;"

delete[]调用数组中每个对象的析构函数,然后释放内存。如果不调用delete[],内存就会泄露。如果调用它多次,则该行为是未定义的(可能导致程序崩溃)。如果您调用delete而不是delete[],则行为是未定义的(可能仅对第一个对象调用析构函数,然后尝试释放内存块-但可以是任何对象)。

3)当你分配结构/类时,operator=被调用。如果你没有为你的结构/类显式定义operator=,那么隐式操作符=生成(它执行你的结构/类的每个非静态成员的赋值)。