delete 与 delete [] 的区别
- 针对对象类型
- delete:用于释放单个对象的内存。例如,如果通过
new
分配了一个普通的类对象:
class MyClass {
public:
MyClass() {}
~MyClass() {}
};
MyClass* obj = new MyClass();
delete obj;
- delete []:用于释放数组形式的对象内存。当使用
new[]
分配内存时,必须使用 delete []
来释放。例如:
MyClass* arr = new MyClass[10];
delete [] arr;
- 调用析构函数的次数
- delete:只调用一次对象的析构函数,因为它处理的是单个对象。
- delete []:会根据数组的大小,对数组中的每个元素调用析构函数。这是因为数组中的每个对象都需要被正确销毁。
- 内存释放方式
- 编译器在使用
new
和 new[]
分配内存时,可能会在内存布局上有所不同。delete
知道如何释放单个对象的内存块,而 delete []
知道如何释放包含多个对象的连续内存块,并正确处理每个对象的析构。
在复杂继承体系下的问题及解决方法
- 多重继承
- 问题:在多重继承中,对象的内存布局会变得复杂。如果使用
delete
而不是 delete []
来释放一个通过 new[]
分配的多重继承类对象数组,可能导致部分基类子对象的析构函数未被调用,从而造成内存泄漏。例如:
class Base1 {
public:
Base1() {}
virtual ~Base1() {}
};
class Base2 {
public:
Base2() {}
virtual ~Base2() {}
};
class Derived : public Base1, public Base2 {
public:
Derived() {}
~Derived() {}
};
Derived* arr = new Derived[10];
// 错误使用delete,会导致Base2子对象析构函数未调用
delete arr;
- 解决方法:始终确保使用
delete []
来释放通过 new[]
分配的对象数组。同时,将基类的析构函数声明为 virtual
,这样在通过基类指针删除派生类对象时,能正确调用派生类及其基类的析构函数。
- 虚基类
- 问题:虚基类在内存布局上有特殊之处,主要是为了避免多重继承中的菱形继承问题时出现数据冗余。如果在虚基类存在的情况下,错误地使用
delete
和 delete []
,可能导致内存释放错误或析构函数调用顺序混乱。例如:
class VirtualBase {
public:
VirtualBase() {}
virtual ~VirtualBase() {}
};
class Derived1 : virtual public VirtualBase {
public:
Derived1() {}
~Derived1() {}
};
class Derived2 : virtual public VirtualBase {
public:
Derived2() {}
~Derived2() {}
};
class FinalDerived : public Derived1, public Derived2 {
public:
FinalDerived() {}
~FinalDerived() {}
};
FinalDerived* obj = new FinalDerived();
// 假设错误地使用delete [](应该使用delete)
delete [] obj;
- 解决方法:同样,要准确匹配
new
和 delete
操作符(new
对应 delete
,new[]
对应 delete []
)。并且虚基类的析构函数也应该声明为 virtual
,以确保在对象销毁时,整个继承体系中的析构函数能按正确顺序调用。此外,在使用 new[]
和 delete []
时要特别注意对象数组中元素的类型和继承结构,避免错误操作。