C语言中new和delete是关键字吗?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

C++ 中的 newdelete

newdelete 是 C++ 中用于动态内存管理的操作符,它们在 上分配和释放内存。

c语言 new delete
(图片来源网络,侵删)

new 操作符

new 的作用是在堆上分配一块内存,并返回一个指向该内存起始地址的指针。

语法:

// 分配单个对象
指针类型 指针变量 = new 数据类型;
// 分配单个对象并初始化
指针类型 指针变量 = new 数据类型(初始值);
// 分配一个数组
指针类型 指针变量 = new 数据类型[元素数量];

示例:

#include <iostream>
int main() {
    // 1. 分配一个 int 类型的对象
    int* p1 = new int;
    *p1 = 10;
    std::cout << "p1 points to: " << *p1 << std::endl;
    // 2. 分配一个 int 对象并直接初始化为 20
    int* p2 = new int(20);
    std::cout << "p2 points to: " << *p2 << std::endl;
    // 3. 分配一个包含 5 个元素的 double 数组
    double* p3 = new double[5];
    p3[0] = 1.1;
    p3[1] = 2.2;
    std::cout << "p3[0] is: " << p3[0] << std::endl;
    // ... 后面需要记得 delete ...
    delete p1;
    delete p2;
    delete[] p3; // 注意数组用 delete[]
    return 0;
}

delete 操作符

delete 的作用是释放由 new 分配的内存,防止内存泄漏。

c语言 new delete
(图片来源网络,侵删)

语法:

// 释放单个对象
delete 指针变量;
// 释放数组
delete[] 指针变量;

重要规则:

  • delete 必须与 new 成对出现:用 new 分配的内存,最终一定要用 delete 释放,否则会导致内存泄漏,即这块内存一直被占用,直到程序结束。
  • 匹配使用
    • new 分配的单个对象,必须用 delete 释放。
    • new[] 分配的数组,必须用 delete[] 释放。
    • 绝对不能混用delete[] p1;delete p3; 是错误的,会导致未定义行为,可能引发程序崩溃或数据损坏。
  • 不要重复释放:对同一块内存释放两次也是未定义行为。
  • 空指针安全:对 nullptr 调用 delete 是安全的,但不会做任何事。

newdelete 的工作原理(以 new int 为例)

当你写下 int* p = new int; 时,C++ 编译器实际上在背后做了两件事:

  1. 调用 operator new:这是一个内存分配函数,它的唯一任务是在堆上找到一块足够大的、连续的、未被使用的内存空间,并返回这块内存的起始地址,如果分配失败,它会抛出 std::bad_alloc 异常(而不是返回 NULL,这是 C 语言 malloc 的做法)。
  2. 调用构造函数:如果分配成功,编译器会调用该数据类型的默认构造函数(对于 int 等基本类型,这个步骤通常是空操作,但对于自定义类,会调用其构造函数)。

同样,当你写下 delete p; 时,编译器也做了两件事:

c语言 new delete
(图片来源网络,侵删)
  1. 调用析构函数p 指向的是一个对象(自定义类的实例),编译器会先调用该对象的析构函数,执行一些清理工作(如释放成员指针指向的内存)。
  2. 调用 operator delete:这是一个内存释放函数,它的任务是释放 operator new 分配的那块内存,将其归还给操作系统,以便后续再次分配。

这个“分配内存 + 调用构造函数”“调用析构函数 + 释放内存”的两步过程是 C++ new/delete 相比 C malloc/free 最核心的优势,它使得 C++ 能够完美地管理对象的生命周期。


C++ new/delete vs C malloc/free

这是一个非常重要的对比,能帮助你理解 C++ 内存管理的设计思想。

特性 C++ new / delete C malloc / free
语言 C++ C
本质 操作符 库函数
功能 分配内存
调用构造函数
仅分配内存
调用析构函数
释放内存
仅释放内存
返回值 成功返回指向类型化内存的指针 成功返回 void* (无类型指针),需要手动强制转换
失败处理 抛出 std::bad_alloc 异常 返回 NULL 指针
数组处理 new[] / delete[] 是语法的一部分 需要手动计算数组大小 malloc(n * sizeof(int))
与类型的关系 与类型紧密绑定,知道要构造什么类型 与类型无关,只关心字节数

示例对比:

假设我们有一个简单的 C++ 类:

class MyClass {
public:
    MyClass() { std::cout << "Constructor called" << std::endl; }
    ~MyClass() { std::cout << "Destructor called" << std::endl; }
};

使用 C++ new/delete

MyClass* obj = new MyClass; // 输出: Constructor called
// ... 使用 obj ...
delete obj; // 输出: Destructor called

可以看到,构造和析构函数被正确调用了。

使用 C malloc/free (在 C++ 中也可以用,但不推荐):

MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); // 不会调用构造函数
// ... 使用 obj (obj 的状态是未初始化的) ...
free(obj); // 不会调用析构函数

可以看到,malloc/free 只负责内存的分配和释放,完全忽略了对象的构造和析构。MyClass 的构造函数中做了重要的初始化工作,那么使用 malloc 得到的对象就是“有毒”的,处于无效状态。


现代 C++ 的替代方案:智能指针

手动管理 newdelete 非常容易出错,是 C++ 中最常见的 bug 来源之一(如忘记 delete、重复 delete、指针悬垂等)。

现代 C++ (C++11 及以后) 强烈推荐使用智能指针来管理动态内存,它们可以自动释放内存,从根本上避免了内存泄漏。

std::unique_ptr

  • 特点独占所有权,一个 unique_ptr 在任何时刻都只能指向一个对象,当 unique_ptr 离开其作用域时(例如函数结束),它所指向的内存会被自动 delete 掉。
  • 优点:轻量级,没有额外的性能开销,非常安全。
  • 使用场景:当你需要一个对象,并且这个对象的生命周期应该由其作用域决定时。
#include <iostream>
#include <memory> // 需要包含头文件
void use_unique_ptr() {
    std::cout << "Entering use_unique_ptr" << std::endl;
    // 创建一个 unique_ptr,它自动管理 new 出来的 MyClass 对象
    std::unique_ptr<MyClass> ptr(new MyClass); // 输出: Constructor called
    // 使用 ptr...
    // ptr.get() 可以获取原始指针,但不推荐转移所有权
    // 当函数结束时,ptr 离开作用域,其析构函数会被自动调用
    // 析构函数内部会自动执行 delete ptr;
    // 输出: Destructor called
    std::cout << "Exiting use_unique_ptr" << std::endl;
} // <-- 自动释放内存发生在这里
int main() {
    use_unique_ptr();
    return 0;
}

std::shared_ptr

  • 特点共享所有权,多个 shared_ptr 可以指向同一个对象,它会维护一个引用计数,当最后一个指向该对象的 shared_ptr 被销毁时,内存才会被 delete
  • 优点:适用于需要共享资源所有权的情况,比如在多个数据结构中保存同一个对象的引用。
  • 使用场景:当多个所有者都需要访问同一个对象时。
#include <iostream>
#include <memory>
#include <vector>
void use_shared_ptr() {
    std::cout << "Entering use_shared_ptr" << std::endl;
    std::shared_ptr<MyClass> p1(new MyClass); // 引用计数 = 1, 输出: Constructor called
    {
        std::shared_ptr<MyClass> p2 = p1; // p2 共享所有权,引用计数 = 2
        std::cout << "Inside inner scope, ref count: " << p2.use_count() << std::endl;
    } // p2 离开作用域,引用计数减 1,变为 1,内存不释放。
    std::cout << "Exiting inner scope, ref count: " << p1.use_count() << std::endl;
} // p1 离开作用域,引用计数减 1,变为 0,内存被自动 delete,输出: Destructor called

特性 new / delete malloc / free 智能指针 (unique_ptr, shared_ptr)
语言 C++ C C++ (现代)
安全性 ,极易出错 ,自动管理内存
易用性 低,需手动成对使用 ,RAII 机制
核心功能 内存 + 构造/析构 仅内存 内存 + 构造/析构 + 自动释放
推荐度 仅在需要底层控制时使用 在 C++ 中不推荐 强烈推荐,现代 C++ 的最佳实践

核心建议:

  1. 在 C++ 中,优先使用智能指针 (std::unique_ptrstd::shared_ptr),而不是原始的 newdelete,这是最安全、最现代的内存管理方式。
  2. 如果必须使用原始指针,请务必确保 newdelete 成对出现,并且正确使用 deletedelete[]
  3. mallocfree 是 C 语言的遗产,在 C++ 代码中应尽量避免使用,除非你正在与 C 代码库交互或进行一些底层的、与对象生命周期无关的内存操作。
-- 展开阅读全文 --
头像
dede后台文章列表为何不显示?
« 上一篇 前天
C语言为何没有select case语句?
下一篇 » 前天

相关文章

取消
微信二维码
支付宝二维码

目录[+]