动态创建包含另一个动态创建的结构数组的结构数组时的内存管理

Memory management when dynamically creating an array of structs that contain another dynamically created array of structs

我正在尝试了解与动态结构数组内部的动态结构数组相关的内存问题。我正在动态创建一个结构数组并将其附加到变量 'persons',然后动态创建一个结构数组并将其附加到每个 'persons' 对象内的变量 'pets'。一个简单的例子是这样的:

typedef struct {
  int age;
} Pet;

typedef struct {
  Pet *pets;
} Person

创建 2 个 Object Person 数组并将其附加到变量 'persons'

persons = (Person*) ::operator new(sizeof(Person) * 2);
persons[0]
persons[1]

创建 3 个对象宠物数组并将其附加到 'persons'

对象 #1 内的变量 'pets'
persons[0].pets = (Pet*) ::operator new(sizeof(Pet) * 3);
persons[0].pets[0].age
persons[0].pets[1].age
persons[0].pets[2].age

创建 2 个对象宠物数组并将其附加到 'persons'

的对象 #2 内的变量 'pets'
persons[1].pets = (Pet*) ::operator new(sizeof(Pet) * 2);
persons[1].pets[0].age
persons[1].pets[1].age

如果我动态创建另一个结构数组并将其附加到 'persons' 数组,那么之前附加到变量 'persons' 的数组是否可用?

创建 4 个 Object Person 数组并将其附加到变量 'persons' 以替换之前的 2 个 Object 数组。

persons = (Person*) ::operator new(sizeof(Person) * 4);
persons[0]
persons[1]
persons[2]
persons[3]

如果不是,我是否需要释放 'persons' 数组的每个对象内的每个 'pets' 数组?

这是 Arduino 上的代码 运行。代码工作正常,似乎无限期地重复,没有任何问题,但这并不意味着它是合法的。

// DYNAMIC MEMORY

// OBJECTS
// Pet object
typedef struct {
  char *name;
  int age;
} Pet;
// Person object
typedef struct {
  char *name;
  int age;
  int numberOfPets;
  Pet *pets;          // OBJECT 'Pet' is part of OBJECT 'Person'
} Person;

#define OBJECT_PET    0
#define OBJECT_PERSON 1


// DYNAMIC MEMORY HANDLING
// ARRAY Creation
void* createArray(int numberOfObjects, int typeOfObject) {
  numberOfObjects;
  int memorySize;
  switch (typeOfObject) {
    case OBJECT_PET:
      Serial.print("Creating Pet array of ");
      memorySize = sizeof(Pet) * numberOfObjects;
      break;
    case OBJECT_PERSON:
      Serial.print("Creating Person array of ");
      memorySize = sizeof(Person) * numberOfObjects;
      break;
  }
  Serial.print(numberOfObjects);
  Serial.println(" objects");

  return ::operator new(memorySize);
}

// GLOBAL VARIABLES
Person *persons;
int numberOfPersons;

void createPersonsAndPetsFirst() {
  numberOfPersons = 3;

  // CREATE 3 empty Person objects [0, 1, 2]
  persons = (Person*) createArray(numberOfPersons, OBJECT_PERSON);

  // ENTER Specific Person information for Person #1
  // Person #1 [0]
  persons[0].name = "Larry";
  persons[0].age = 19;
  persons[0].numberOfPets = 2;
  // CREATE 2 empty Pet objects for Person #1
  persons[0].pets = (Pet*) createArray(persons[0].numberOfPets, OBJECT_PET);
  // ENTER Specific Pet information for Pet #1
  // Pet #1
  persons[0].pets[0].name = "Xander";
  persons[0].pets[0].age = 3;
  // ENTER Specific Pet information for Pet #2
  // Pet #2
  persons[0].pets[1].name = "Shorty";
  persons[0].pets[1].age = 6;

  // ENTER Specific Person information for Person #2
  // Persons #2 [1]
  persons[1].name = "Mark";
  persons[1].age = 29;
  persons[1].numberOfPets = 1;
  // CREATE 1 empty Pet object for Person #2
  persons[1].pets = (Pet*) createArray(persons[1].numberOfPets, OBJECT_PET);
  // ENTER Specific Pet information for Pet #1
  // Pet #1 [0]
  persons[1].pets[0].name = "Fido";
  persons[1].pets[0].age = 5;

  // ENTER Specific Person information for Person #3
  // Person #3 [2]
  persons[2].name = "Larry";
  persons[2].age = 19;
  persons[2].numberOfPets = 2;
  // CREATE 2 empty Pet objects for Person #1
  persons[2].pets = (Pet*) createArray(persons[0].numberOfPets, OBJECT_PET);
  // ENTER Specific Pet information for Pet #1
  // Pet #1
  persons[2].pets[0].name = "Nado";
  persons[2].pets[0].age = 12;
  // ENTER Specific Pet information for Pet #2
  // Pet #2
  persons[2].pets[1].name = "Buster";
  persons[2].pets[1].age = 4;

  Serial.println();
}

void createPersonsAndPetsSecond() {
  numberOfPersons = 2;

  // CREATE 2 empty Person objects [0, 1]
  persons = (Person*) createArray(numberOfPersons, OBJECT_PERSON);

  // ENTER Specific Person information for Person #1
  // Person #1 [0]
  persons[0].name = "Chad";
  persons[0].age = 22;
  persons[0].numberOfPets = 1;
  // CREATE 1 empty Pet object for Person #1
  persons[0].pets = (Pet*) createArray(persons[0].numberOfPets, OBJECT_PET);
  // ENTER Specific Pet information for Pet #1
  // Pet #1
  persons[0].pets[0].name = "Lucky";
  persons[0].pets[0].age = 5;

  // ENTER Specific Person information for Person #2
  // Persons #2 [1]
  persons[1].name = "Lisa";
  persons[1].age = 36;
  persons[1].numberOfPets = 2;
  // CREATE 2 empty Pet objects for Person #2
  persons[1].pets = (Pet*) createArray(persons[1].numberOfPets, OBJECT_PET);
  // ENTER Specific Pet information for Pet #1
  // Pet #1 [0]
  persons[1].pets[0].name = "Chester";
  persons[1].pets[0].age = 7;
  // ENTER Specific Pet information for Pet #2
  // Pet #2 [1]
  persons[1].pets[1].name = "Marlo";
  persons[1].pets[1].age = 12;

  Serial.println();
}

void sendPersonsAndPetsInformationToHost() {
  Serial.print("Number of Persons = ");
  Serial.println(numberOfPersons);

  for (int personsIndex = 0; personsIndex < numberOfPersons; personsIndex++) {
    // SHOW Person
    Serial.print("Person #");
    Serial.print(personsIndex + 1);
    Serial.println(" Information");

    // SHOW Person Information
    Serial.print("Name = ");
    Serial.println(persons[personsIndex].name);
    Serial.print("Age  = ");
    Serial.println(persons[personsIndex].age);

    // SHOW Person number of Pets Information
    Serial.print("Number of Pets = ");
    Serial.println(persons[personsIndex].numberOfPets);

    for (int petsIndex = 0; petsIndex < persons[personsIndex].numberOfPets; petsIndex++) {
      // SHOW Pet
      Serial.print("Pet #");
      Serial.print(petsIndex + 1);
      Serial.println(" Information");

      Serial.print("Name = ");
      Serial.println(persons[personsIndex].pets[petsIndex].name);
      Serial.print("Age  = ");
      Serial.println(persons[personsIndex].pets[petsIndex].age);
    }
    Serial.println();
  }
}


void setup() {
  // OPEN Serial communication
  Serial.begin(115200);

}

void loop() {
  // CREATE First Persons and Pets Arrays
  createPersonsAndPetsFirst();

  // SEND Persons and Pets array information to Host
  sendPersonsAndPetsInformationToHost();

  delay(2000);

  // CREATE Second Persons and Pets Arrays
  createPersonsAndPetsSecond();

  // SEND Persons and Pets array information to Host
  sendPersonsAndPetsInformationToHost();

  delay(2000); 

}

NO 之前分配的内存不会被释放。您将失去所有分配的内存。

通过覆盖您的 persons 指针,您将失去对已分配存储的句柄。所以在你这样做之前,你必须调用

::operator delete(persons[0].pets);
::operator delete(persons[1].pets);
::operator delete(persons);

这很丑,不是吗?因此存在 std::vector 容器。

#include <vector>

struct Person {
  std::vector< Pet > pets;
};

std::vector< Person > persons(2);
persons[0].pets = std::vector< Pet >(2);
persons[1].pets = std::vector< Pet >(3);

persons = std::vector< Person >(3); // won't lose memory

由于您可能没有听说过,我建议您继续阅读 RAII or here

总之,不行,你需要删除它们。

您创建的第一个 Person 数组将变成垃圾,因此您应该在将另一个数组分配给指针之前将其删除(释放)。 (将 "new" 运算符视为访问内存位置的请求权限(密钥),因此如果您丢失了该位置的地址,您将无法再使用它,其他人也不能(因为您有钥匙!)所以它变成了垃圾。)

现在,如果删除 Person 数组,Pet 数组仍然是垃圾,所以您应该先删除它们(否则您没有它们的地址,而且还是垃圾!),然后删除 Person 数组。

更好的(也是标准的)方法是为Person写一个析构方法,删除里面的Pet数组。删除 Person 对象时将自动调用析构函数。