深入理解 C++ 中的 delete

目录

  1. 基础概念
  2. 使用方法
  3. 常见实践
  4. 最佳实践
  5. 小结

基础概念

在 C++ 中,delete 是一个操作符,用于释放通过 new 操作符分配的动态内存。当使用 new 创建一个对象时,它会在堆上分配内存并返回一个指向该对象的指针。使用完对象后,需要使用 delete 来释放这块内存,以避免内存泄漏。

使用方法

删除单个对象

当通过 new 创建一个单个对象时,使用 delete 来释放其内存。例如:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    MyClass* myObject = new MyClass();
    // 使用 myObject
    delete myObject; // 释放内存并调用析构函数
    return 0;
}

在上述代码中,new MyClass() 在堆上创建一个 MyClass 对象,并返回一个指向它的指针 myObject。当使用 delete myObject 时,对象的析构函数会被调用,然后内存被释放。

删除对象数组

如果使用 new[] 创建了一个对象数组,那么需要使用 delete[] 来释放内存。例如:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    MyClass* myArray = new MyClass[3];
    // 使用 myArray
    delete[] myArray; // 释放数组内存并为每个对象调用析构函数
    return 0;
}

这里 new MyClass[3] 创建了一个包含 3 个 MyClass 对象的数组。delete[] myArray 会为数组中的每个对象调用析构函数,然后释放整个数组的内存。

常见实践

与 new 配对使用

delete 必须与 new 正确配对使用。如果 new 用于分配内存,那么对应的 delete 必须在不再需要该内存时被调用。例如:

#include <iostream>

void someFunction() {
    int* number = new int;
    *number = 42;
    // 做一些其他操作
    delete number; // 释放内存
}

int main() {
    someFunction();
    return 0;
}

someFunction 函数中,new int 分配了内存,最后 delete number 释放了该内存,确保不会发生内存泄漏。

处理继承关系中的对象删除

在处理继承关系时,当通过基类指针删除派生类对象时,基类的析构函数必须声明为虚函数。例如:

#include <iostream>

class Base {
public:
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    delete basePtr; // 正确调用派生类析构函数
    return 0;
}

如果基类的析构函数不是虚函数,delete basePtr 只会调用基类的析构函数,而不会调用派生类的析构函数,可能导致资源泄漏。

最佳实践

智能指针的使用

现代 C++ 推荐使用智能指针(如 std::unique_ptrstd::shared_ptr)来管理动态内存,而不是直接使用 delete。智能指针会自动处理内存的释放,减少了手动调用 delete 时出错的可能性。例如:

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    std::unique_ptr<MyClass> myObject = std::make_unique<MyClass>();
    // 使用 myObject
    // 离开作用域时,myObject 会自动释放内存
    return 0;
}

std::unique_ptr 会在其作用域结束时自动调用 delete,无需手动干预。

确保正确的对象生命周期管理

在复杂的程序中,确保对象的创建和删除时机正确非常重要。可以使用资源获取即初始化(RAII)原则,将资源的生命周期与对象的生命周期绑定。例如,智能指针就是基于 RAII 原则实现的。

小结

delete 是 C++ 中释放动态内存的重要操作符。正确使用 deletenew 配对,特别是在处理对象数组和继承关系时,是避免内存泄漏的关键。然而,现代 C++ 更倾向于使用智能指针来管理动态内存,以提高代码的安全性和可维护性。通过理解 delete 的基础概念、使用方法、常见实践以及最佳实践,开发者可以更好地管理内存,编写更健壮的 C++ 代码。