内存泄漏产生原因
1. 存在基类A和子类B时,当子类B中有动态分配的指针ptr存在,B的析构函数里对ptr进行释放,将子类B的实例赋值给基类A的指针,当使用基类指针来删除对象时,如果基类没有虚析构函数,那么会导致子类的析构函数不被调用,从而造成资源泄漏或其他问题。因此,当基类被用作多态基类时,通常应该为其定义虚析构函数;
2.
class A
{
A() : a(new int[5]) { }
~A() { delete a; }
int* a;
}
int* a; a = new int[5]; 这两行代码创建了一个指向整数的指针a,并通过new int[5]动态分配了一个包含5个整数的数组。然后,a被赋值为这个新分配的内存的地址。
接下来析构函数里的delete a;代码行试图释放这块内存。然而,这里存在一个严重的错误:应该使用delete[] a;来释放整个数组,而不是delete a;。
当你使用delete a;而不是delete[] a;时,你实际上告诉编译器你只释放了一个单独的对象,而不是一个数组。编译器只会释放指针a所指向的内存地址上的那块内存,而不会考虑整个数组。这通常会导致内存泄漏,因为编译器不会释放数组剩余部分的内存。
更重要的是,如果这块内存后面还有其他由new或new[]分配的内存块,那么使用delete而不是delete[]可能会破坏内存管理的数据结构(如内存分配器中的链表或位图),从而导致不可预测的行为,包括程序崩溃或数据损坏。
因此,delete a;并没有只释放数组的第一个元素,而是尝试以错误的方式释放整个数组的内存,这通常会导致未定义行为。正确的做法是使用delete[] a;来释放整个数组的内存。
为了避免这类错误,一定要记住:对于通过new分配的单个对象,使用delete来释放;对于通过new[]分配的数组,使用delete[]来释放。同时,在释放内存后,将指针设置为nullptr是一个好习惯,以避免悬挂指针的问题。
补充:
delete a;尝试释放指针a所指向的整个内存块,而不仅仅是第一个元素的内存。然而,由于a实际上指向的是一个通过new int[5]分配的数组,因此应该使用delete[] a;来释放整个数组。
当使用delete a;时,编译器并不知道a实际上指向的是一个数组。它只会尝试释放a指向的那块内存的开始部分,并假定这块内存是一个单独分配的对象。这意味着它不会考虑数组中可能存在的其他元素或数组大小等任何额外的信息。因此,尽管delete a;试图释放整个内存块,但由于使用了错误的操作符,它不会正确地处理整个数组的内存释放。
实际上,delete a;会释放掉a指向的内存块的起始部分(也可以理解为释放了数组的第一个元素),但由于没有正确处理数组的情况,剩余的内存块(即数组中除了第一个元素以外的其他元素)将不会被正确释放。这会导致内存泄漏,因为那些未被释放的内存块将仍然被占用,但无法再被程序访问或管理。
此外,由于内存管理结构可能被破坏(例如,如果后续有其他内存分配操作覆盖了未被释放的数组内存),还可能导致数据损坏或程序崩溃等未定义行为。
因此,为了确保整个数组的内存被正确释放,应该在析构函数中使用delete[] a;。这样,编译器会知道它正在处理一个数组,并会释放整个数组占用的内存,包括所有元素。