C++delete详解剖析
delete的原理
- 在空间上执行析构函数,完成对象中资源的清理工作
- 调用operator delete函数释放对象的空间
在C++中,delete
操作符的执行过程涉及两个主要步骤:首先调用对象的析构函数来清理资源,然后通过 operator delete
函数释放分配给对象的内存。这两个步骤的具体行为取决于几个因素,包括对象是否是通过 new
动态分配的、对象是否是数组的一部分、以及类是否定义了虚析构函数等。
1. 调用析构函数
决定调用哪个析构函数的因素:
-
对象的实际类型:如果通过基类指针指向派生类对象,并且该基类的析构函数被声明为虚函数,则会根据对象的实际类型(即派生类)调用相应的析构函数。这是实现多态性的重要部分。(这里会感到疑惑不满足多态三同条件,但是编译器会对所有析构函数进行处理,处理成三同)
-
非虚析构函数的情况:如果基类的析构函数不是虚函数,那么即使通过基类指针指向派生类对象,也只会调用基类的析构函数,这可能导致派生类的部分没有被正确销毁,从而引发资源泄漏等问题。
- 示例:
在这个例子中,由于class Base { public:virtual ~Base() { std::cout << "Base destructor" << std::endl; } };class Derived : public Base { public:~Derived() override { std::cout << "Derived destructor" << std::endl; } };int main() {Base* ptr = new Derived();delete ptr; // 输出: Derived destructor, Base destructorreturn 0; }
Base
的析构函数是虚函数,当删除ptr
时,程序首先调用了Derived
类的析构函数,然后才调用Base
类的析构函数。
- 示例:
2. 调用 operator delete
函数释放内存
一旦对象的析构函数被调用并完成其工作后,接下来就是释放这块内存。这里涉及到 operator delete
函数的调用:
-
默认
operator delete
:如果没有特别定义operator delete
,则使用全局或类特定的operator delete
来释放内存。默认情况下,C++标准库提供了一个全局的operator delete
函数,它负责将内存返回给系统。- 示例:
MyClass* obj = new MyClass(); delete obj; // 默认的 operator delete 被调用来释放内存
- 示例:
-
自定义
operator delete
:你可以为你的类重载operator delete
,以便以不同的方式管理内存(例如,使用内存池)。- 示例:
class MyClass { public:void* operator new(size_t size) {std::cout << "Custom allocation" << std::endl;return ::operator new(size);}void operator delete(void* ptr) noexcept {std::cout << "Custom deallocation" << std::endl;::operator delete(ptr);} };int main() {MyClass* obj = new MyClass();delete obj; // 使用自定义的 operator deletereturn 0; }
- 示例:
数组与单个对象的区别
对于动态分配的数组,你需要使用 delete[]
而不是 delete
。delete[]
不仅会调用数组中每个元素的析构函数(如果有的话),还会确保整个数组占用的内存块被正确释放。
-
注意:不要混淆
delete
和delete[]
。错误地使用它们会导致未定义的行为。- 示例:
MyClass* array = new MyClass[3]; delete[] array; // 正确做法
- 示例:
总结
-
调用析构函数:由对象的实际类型决定,特别是在使用基类指针指向派生类对象时,若基类析构函数为虚函数,则会根据对象的实际类型调用正确的析构函数;否则,默认只调用基类的析构函数。
-
释放内存:通过
operator delete
完成,可以是默认的也可以是用户自定义的版本。
理解这些机制有助于更有效地管理和优化内存使用,同时避免常见的内存管理错误如内存泄漏和双重释放等。