C++ 基础

C++ 主页 C++ 概述 C++ 环境设置 C++ 基本语法 C++ 注释 C++ Hello World C++ 省略命名空间 C++ 标记 C++ 常量/字面量 C++ 关键字 C++ 标识符 C++ 数据类型 C++ 数字数据类型 C++ 字符数据类型 C++ 布尔数据类型 C++ 变量类型 C++ 变量作用域 C++ 多变量 C++ 基本输入/输出 C++ 修饰符类型 C++ 存储类 C++ 数字 C++ 枚举 C++ 枚举类 C++ 引用 C++ 日期和时间

C++ 运算符

C++ 运算符 C++ 算术运算符 C++ 关系运算符 C++ 逻辑运算符 C++ 位运算符 C++ 赋值运算符 C++ sizeof 运算符 C++ 条件运算符 C++ 逗号运算符 C++ 成员运算符 C++ 强制类型转换运算符 C++ 指针运算符 C++ 运算符优先级 C++ 一元运算符

C++ 控制语句

C++ 决策语句 C++ if 语句 C++ if else 语句 C++ 嵌套 if 语句 C++ switch 语句 C++ 嵌套 switch语句 C++ 循环类型 C++ while 循环 C++ for 循环 C++ do while 循环 C++ Foreach 循环 C++ 嵌套循环 C++ break 语句 C++ continue 语句 C++ goto 语句

C++ 字符串

C++ 字符串 C++ 循环遍历字符串 C++ 字符串长度 C++ 字符串连接 C++ 字符串比较

C++ 函数

C++ 函数 C++ 多函数参数 C++ 递归函数 C++ 返回值 C++ 函数重载 C++ 函数重写 C++ 默认参数

C++ 数组

C++ 数组 C++ 多维数组 C++ 指向数组的指针 C++ 将数组传递给函数 C++ 从函数返回数组

C++ 结构 &联合

C++ 结构 C++ 联合

C++ 指针

C++ 指针 C++ 解引用 C++ 修改指针

C++ 类和对象

C++ 面向对象 C++ 类 &对象 C++ 类成员函数 C++ 类访问修饰符 C++ 静态类成员 C++ 静态数据成员 C++ 静态成员函数 C++ 内联函数 C++ this 指针 C++ 友元函数 C++ 指向类的指针

C++ 构造函数

C++ 构造函数 &析构函数 C++ 默认构造函数 C++ 参数化构造函数 C++ 复制构造函数 C++ 构造函数重载 C++ 带默认参数的构造函数 C++ 委托构造函数 C++ 构造函数初始化列表 C++ 使用构造函数动态初始化

C++ 继承

C++ 继承 C++ 多重继承 C++ 多级继承

C++ 面向对象

C++ 重载 C++ 多态性 C++ 抽象 C++ 封装 C++ 接口 C++ 虚函数 C++ 纯虚函数与抽象类

C++ 文件处理

C++ 文件和流 C++ 文件读取

C++ 进阶

C++ 异常处理 C++ 动态内存 C++ 命名空间 C++ 模板 C++ 预处理器 C++ 信号处理 C++ 多线程 C++ Web 编程 C++ 套接字编程 C++ 并发 C++ 高级概念 C++ Lambda 表达式 C++ unordered_multiset

C++ 实用资源

C++ 问答 C++ 快速指南 C++ 速查表 C++ STL 教程 C++ 标准库 C++ 实用资源 C++ 讨论


C++ 动态内存

要成为一名优秀的 C++ 程序员,充分理解 C++ 中动态内存的工作原理至关重要。C++ 程序中的内存分为两部分:

  • - 函数内声明的所有变量都会占用栈中的内存。

  • - 这是程序中未使用的内存,可用于在程序运行时动态分配内存。

很多时候,您无法提前知道在定义的变量中存储特定信息需要多少内存,所需内存的大小可以在运行时确定。

您可以使用 C++ 中的特殊运算符在运行时在堆中为给定类型的变量分配内存,该运算符返回分配空间的地址。此运算符称为 new 运算符。

如果您不再需要动态分配的内存,可以使用 delete 运算符,它会取消分配先前由 new 运算符分配的内存。

new 和 delete 运算符

以下是使用 new 运算符为任何数据类型动态分配内存的通用语法。

new data-type;

这里的 data-type 可以是任何内置数据类型(包括数组)或任何用户定义的数据类型(包括类或结构体)。我们先从内置数据类型开始。例如,我们可以将指针定义为 double 类型,然后在执行时请求分配内存。我们可以使用 new 运算符来实现这一点,如下所示:-

double* pvalue = NULL; // 指针初始化为 null
pvalue = new double; // 为变量申请内存

如果空闲存储空间已用完,则可能未成功分配内存。因此,最好检查 new 运算符是否返回 NULL 指针,并采取以下适当措施:-

double* pvalue  = NULL;
if( !(pvalue  = new double )) {
   cout << "Error: out of memory." <<endl;
   exit(1);
}

C 语言中的 malloc() 函数在 C++ 中仍然存在,但建议避免使用 malloc() 函数。new 函数相对于 malloc() 函数的主要优势在于,new 函数不仅仅是分配内存,它还能构造对象,而这正是 C++ 的主要用途。

在任何时候,当您觉得不再需要某个动态分配的变量时,都可以使用 delete 运算符在空闲存储区中释放它所占用的内存,如下所示:-

delete pvalue; // 释放 pvalue 指向的内存

让我们将上述概念结合起来,并通过以下示例来展示 new 和 delete 的工作原理:-

#include <iostream>
using namespace std;

int main () {
    double* pvalue = NULL; // 指针初始化为 null
    pvalue = new double; 	// 为变量申请内存
    
    *pvalue = 29494.99; 	// 将值存储在分配的地址
    cout << "Value of pvalue : " << *pvalue << endl;
    
    delete pvalue; 			// 释放内存。
    
    return 0;
}

如果我们编译并运行上述代码,将产生以下结果 -

Value of pvalue : 29495

数组的动态内存分配

假设您要为一个字符数组(即包含 20 个字符的字符串)分配内存。使用与上面相同的语法,我们可以动态分配内存,如下所示。

char* pvalue = NULL; 		// 指针初始化为 null
pvalue = new char[20]; 		// 为变量申请内存

要删除刚刚创建的数组,语句如下:-

delete [] pvalue; 			// 删除 pvalue 指向的数组

遵循与 new 运算符类似的通用语法,您可以按如下方式为多维数组分配内存 -

double** pvalue = NULL; 	// 指针初始化为 null
pvalue = new double [3][4]; // 为 3x4 数组分配内存

但是,释放多维数组内存的语法仍然与上述相同 -

delete [] pvalue; 			// 删除 pvalue 指向的数组

对象的动态内存分配

对象与简单数据类型没有区别。例如,考虑以下代码,我们将使用对象数组来阐明概念 -

#include <iostream>
using namespace std;

class Box {
   public:
      Box() { 
         cout << "Constructor called!" <<endl; 
      }
      ~Box() { 
         cout << "Destructor called!" <<endl; 
      }
};
int main() {
   Box* myBoxArray = new Box[4];
   delete [] myBoxArray; // 删除数组

   return 0;
}

如果要分配一个包含四个 Box 对象的数组,则 Simple 构造函数将被调用四次;同样,在删除这些对象时,析构函数也将被调用相同次数。

如果我们编译并运行上述代码,将产生以下结果 -

Constructor called!
Constructor called!
Constructor called!
Constructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!