当前位置: 首页 > article >正文

C++-------动态内存管理

堆 - 栈图

在 C++ 中,栈和堆是两块不同的内存区域,有着不同的特性:

  • 栈(Stack)
    • 是一块连续的内存区域,由编译器自动管理。函数调用时,局部变量、函数参数等都会被压入栈中,函数结束时,相应的数据会按顺序弹出栈,内存自动回收。例如:
void func() {
    int localVar = 10; // localVar 存储在栈上
}
- 栈内存分配和释放速度快,因为它的管理机制简单、规整,但大小通常有限制,取决于编译器和操作系统。
  • 堆(Heap)
    • 是一块相对较大、不连续的内存区域,用于动态分配内存。程序员使用 newmalloc 等操作符来申请堆内存,使用完毕后需手动用 deletefree 释放,否则容易造成内存泄漏。例如:
int* ptr = new int; // 在堆上分配内存
*ptr = 20;
delete ptr; 
- 堆内存的优点是灵活性高,可以按需分配较大的内存块,但分配和释放速度比栈慢,且管理难度较大。

下面是一个简单的堆 - 栈示意图:

内存区域管理方式分配速度内存连续性典型用途
编译器自动管理连续局部变量、函数参数
程序员手动管理不连续动态分配大内存块、对象生存期控制

拷贝对象

  • 浅拷贝
    • 简单地复制对象的值,对于指针成员,只是复制指针的值,而不是指针指向的数据。这意味着多个对象的指针成员会指向同一块内存。例如:
class MyClass {
public:
    int* data;
    MyClass() {
        data = new int(10);
    }
    MyClass(const MyClass& other) {
        data = other.data;
    }
};
- 浅拷贝的问题在于,如果一个对象释放了指针指向的内存,其他共享该指针的对象就会出现悬空指针。

在这里插入图片描述

  • 深拷贝
    • 不仅复制对象的值,还递归地复制指针成员指向的数据。这样每个对象都有独立的、完整的数据副本。例如:
class MyClass {
public:
    int* data;
    MyClass() {
        data = new int(10);
    }
    MyClass(const MyClass& other) {
        data = new int(*other.data);
    }
};
- 深拷贝确保了对象间的独立性,但代价是额外的内存和时间开销,因为要复制深层次的数据结构。
  • 赋值和拷贝构造函数
    • 拷贝构造函数:用于创建一个新对象,初始化为已有对象的副本,语法形式为 ClassName(const ClassName& other)。上面的 MyClass(const MyClass& other) 就是拷贝构造函数示例。
    • 赋值运算符:用于将一个已有对象的值赋给另一个已存在的对象,语法形式为 ClassName& operator=(const ClassName& other)。例如:
class MyClass {
public:
    int* data;
    MyClass& operator=(const MyClass& other) {
        if (this!= &other) {
            delete data;
            data = new int(*other.data);
        }
        return *this;
    }
};

关键字 const 的使用

  • 修饰变量const 修饰的变量值不能被修改,一旦初始化就固定了。例如:const int num = 10;num 在后续代码中无法重新赋值。
  • 修饰指针
    • const int* ptr:指针指向的内容是常量,不能通过该指针修改所指的值,但指针本身可以指向其他地方。
    • int* const ptr:指针本身是常量,不能指向其他地方,但可以修改指针指向的值。
    • const int* const ptr:指针和它指向的内容都是常量。
  • 修饰函数参数:表示函数不会修改传入的参数,这有助于代码可读性和编译器优化,同时防止意外修改。例如:void print(const std::string& str),函数内部不能修改 str
  • 修饰函数返回值:当返回值为对象或指针时,用 const 修饰可以防止调用者意外修改返回值,常用于返回类的成员变量。

charstack 类的效率

  • 空间效率
    • 如果采用动态数组实现 charstack 类,如之前代码示例,可能会存在一定的空间浪费。因为动态数组每次扩容时,通常是翻倍扩容,扩容后的部分在短时间内可能闲置。例如,初始容量为 10,扩容到 20 后,可能栈里只有 11 个元素,剩余 9 个空间未使用。
  • 时间效率
    • 入栈(push):平均情况下,入栈操作时间复杂度为 O ( 1 ) O(1) O(1),但在扩容时,需要复制旧数组元素到新数组,时间复杂度变为 O ( n ) O(n) O(n),其中 n n n 是当前栈中元素个数 。
    • 出栈(pop):时间复杂度为 O ( 1 ) O(1) O(1),只需调整栈顶指针即可完成出栈操作。
    • 判断空满:时间复杂度均为 O ( 1 ) O(1) O(1),只涉及简单的指针或索引比较。如果想进一步提升效率,可以考虑采用链表结构实现栈,避免数组扩容带来的额外开销,但链表实现会增加指针操作的复杂性。
      在这里插入图片描述

http://www.kler.cn/a/452935.html

相关文章:

  • LDR6020在iPad一体式键盘的创新应用
  • Spring提供了很好事务管理机制
  • Mac提示:安装anycast后台服务进程失败
  • STM32之GPIO输出与输出
  • Vivado常用IP例化2
  • 新能源汽车双向峰谷充放电研究
  • 查看mysql编译参数
  • IT运维中的自然语言处理(NLP)技术应用
  • php8.0版本更新了哪些内容
  • react useCallback
  • Python字符串及正则表达式(十一):正则表达式、使用re模块实现正则表达式操作
  • goview——vue3+vite——数据大屏配置系统
  • 架构师之路--达梦数据库执行计划与索引优化
  • 【ETCD】【实操篇(十)】基于 ETCD 实现一个简单的服务注册及发现功能
  • VScode实时检查c++语法错误
  • 【STM32 Modbus编程】-作为从设备写入寄存器
  • Linux 中检查 Apache Web Server (httpd) 正常运行时间的 4 种方法
  • 开源轮子 - HTTP Client组件
  • 关于Qt中query.addBindValue()和query.bindValue()报错:Parameter count mismatch
  • 深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 (中英双语)