堆和栈的区别——vector引发的思考

堆和栈的区别——vector引发的思考


前言

在使用vector这个结构的时候,如果vector在函数内部直接定义,则对象存储在栈上,数据存储在堆上;而通过new动态创建时,指针在栈上,对象和数据都在堆上。同时,at()方法提供了越界检查,但会带来额外的时间开销。无论哪种方式,vector的数据总是位于堆上。那么为什么会有堆和栈呢?本文将介绍相关内容。

一、堆和栈的区别

堆用来补充栈的功能。‌

栈是一种后进先出(‌LIFO)‌的数据结构,‌它允许我们在数据结构的一端进行数据的添加(‌push)‌和移除(‌pop)‌操作。‌栈在函数调用、‌表达式求值等场景中发挥着重要作用。‌然而,‌栈的内存空间是预先分配且大小固定的,‌这意味着当函数局部变量或递归调用占用的内存超过栈区大小时,‌就会导致栈溢出,‌进而可能导致程序崩溃。‌因此,‌为了解决这个问题,‌我们需要使用堆来补充栈的功能,栈的大小通常比较小且固定,内存分配由系统自动管理。‌

堆是一种动态内存分配机制,‌它允许程序在运行时申请和释放内存。‌与栈不同,‌堆提供的内存空间是动态扩展的,‌可以满足程序运行时的动态内存需求。‌然而,‌使用堆需要程序员手动管理内存,‌包括分配和释放,‌这增加了编程的复杂性,‌并可能导致内存泄漏等问题。‌尽管如此,‌堆的使用仍然是必要的,‌因为它提供了更大的灵活性,‌可以处理那些无法预先确定大小的数据结构或变量,堆的大小通常比栈大,并且可以动态增长和收缩,有效弥补栈空间不足的缺点

总的来说,‌虽然栈在内存管理中具有高效和可靠的优势,‌但它的固定大小限制了其应用场景。‌为了解决这个问题,‌我们需要使用堆来补充栈的功能,‌尽管堆的使用增加了编程的复杂性,‌但它提供了更大的灵活性和对动态内存的需求。‌

通俗的讲,堆上分配内存自由,给任何变量分配空间,该变量都不用关心其他变量何时释放,但是栈不行,栈是先进后出,任何变量必须等他上方的变量释放了才能释放,这意味着即便有先分配的对象先结束生命周期,也要等到后分配对象结束生命周期才能释放空间。

具体到程序中,函数的局部变量的生命周期刚好满足栈的要求:被调用函数的局部变量肯定比调用方局部变量更早释放,栈本身可以看成是针对局部变量的内存分配优化。

栈分配和释放只要移动栈顶指针,堆分配需要跑一圈分配算法,甚至要扫描空闲空间来寻找合适大小的块,堆释放也可能涉及融合相邻的空闲块,所以堆分配很慢,比栈慢的多。

因此,‌在编程实践中,‌我们通常会结合使用栈和堆,‌以充分利用它们的优点并避免各自的缺点

二、内存的静态分配和动态分配区别

内存的静态分配和动态分配区别:

1.时间不同:

静态分配发生在程序的编译和链接的时候。
动态分配发生在程序调入和执行的时候。

2.空间不同:

静态分配只能是有栈来分配(有编译器来完成,比如定义一个局部变量 int b = 1)
动态分配可以是堆分配(malloc分配,需要手动回收内存)或者栈分配(编译器来完成,自动回收内存)

3.灵活度不同:

静态分配需要提前指定空间大小,不能再动态改变大小。
动态分配不需要提前分配存储空间,可以动态的调整大小。

4.生命周期不同:

静态分配的内存在程序一开始运行就会分配内存,直到程序结束了,内存才会被释放。
动态分配的内存是在程序调用函数时才被分配,函数结束了,动态内存就应该被释放掉(别忘了手动释放)。

三、智能指针

C++ 智能指针是C++标准库提供的类模板,用于自动管理动态分配的对象的生命周期。它们旨在解决传统原始指针可能导致的内存泄漏和资源未正确释放等问题,通过封装对动态内存的访问和控制,实现自动化的资源清理。以下是对C++中几种主要智能指针类型的详细介绍:

1. std::unique_ptr (C++11)

原理与特点:表现为独占所有权(exclusive ownership)的智能指针。任何时候只有一个unique_ptr实例可以拥有并管理某个对象。

当unique_ptr离开其作用域或被显式重置时,它会自动删除其所指向的对象。
不支持复制构造函数和赋值操作符,但可以通过转移语义(move semantics)进行移动构造和移动赋值,转移过程中原unique_ptr变为空指针,所有权转移到新unique_ptr。
可以直接或间接管理非数组类型以及数组类型。

成员函数:

release():释放所有权,返回指向对象的原始指针,之后unique_ptr变为空。
reset():释放当前所拥有的对象(如果存在),可选地接受一个新的裸指针来接管。
get():返回指向对象的原始指针,但不改变所有权。
operator->() 和 operator*():提供对托管对象的直接访问。

#include <memory> 
// 所在的头文件
// 1.创建一个指向整型对象的unique_ptr,默认使用delete运算符释放资源
std::unique_ptr<int> uptr(new int(10));

// 2.C++ 14 使用 std::make_unique
std::unique_ptr<int> uptr = std::make_unique<int>(10);
std::unique_ptr<MyClass> uptr = std::make_unique<MyClass>();
// 3.转移所有权
int* raw_ptr = new int(10);
std::unique_ptr<int> uptr(raw_ptr); 
// 如果uptr已有对象,先释放旧对象,再转移所有权
uptr.reset(raw_ptr);

2. std::shared_ptr (C++11)

原理与特点:实现共享所有权(shared ownership)的智能指针。多个shared_ptr实例可以同时指向并共享同一个对象。

内部维护一个引用计数(reference count)。每当新的shared_ptr拷贝或赋值现有shared_ptr时,引用计数递增;当shared_ptr销毁或重置时,引用计数递减。当引用计数降至零时,自动删除所管理的对象。
支持弱引用计数,通过std::weak_ptr关联到同一对象,但不会增加引用计数。

成员函数:

use_count():返回当前共享对象的引用计数值。
reset():释放当前所拥有的对象(如果存在),可选地接受一个新的裸指针来接管。
get():返回指向对象的原始指针,但不改变所有权。
operator->() 和 operator*():提供对托管对象的直接访问。
swap():交换两个shared_ptr的内容。
与std::make_shared一起使用可以提高效率和减少内存碎片。

#include <memory> 
int a = new int(100);
std::shared_ptr ptr(a); 
// 通过make_shared函数得到
std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
// 拷贝初始化
std::shared_ptr<int> ptr2(ptr1);
//std::shared_ptr<int> ptr2 = ptr1;这样赋值是错误的,只要是智能指针,这样直接用=赋值是有问题的必须std::shared_ptr<int> ptr2(ptr1);

四、unique_ptr和shared_ptr的区别

所有权机制:‌

unique_ptr 拥有对象的所有权,‌即它独占对象资源,‌保证没有任何其他 unique_ptr 或 shared_ptr指向同一对象。‌这种独占性确保了资源管理的唯一性,‌防止了资源竞争和意外释放的问题。‌

shared_ptr则允许多个智能指针共享同一个对象的所有权。‌它通过引用计数来实现,‌当引用计数变为0时,‌对象才会被删除。‌这种机制适用于需要多个指针共同管理对象资源的情况,‌避免了资源的提前释放。‌

控制机制:‌

unique_ptr 通过值传递,‌提供了移动语义,‌允许在多个 unique_ptr之间转移所有权,‌但不允许复制。‌这种机制保证了资源的唯一性和排他性,‌适用于需要明确所有权转移的场景。‌

shared_ptr 除了提供基本的指针功能外,‌还允许放入标准容器中,‌因为它满足了标准容器对元素的要求。‌通过引用计数和自定义删除器,‌shared_ptr 能够在多个指针间共享对象所有权,‌同时自动管理对象的生命周期。‌

使用场景:‌

当需要明确资源所有权和防止资源竞争时,‌应使用 unique_ptr。‌例如,‌在函数返回对象时,‌可以通过移动语义将所有权转移给调用者,‌确保资源的正确管理。‌

当需要多个指针共同管理同一资源,‌或者需要将智能指针放入标准容器时,‌应使用 shared_ptr。‌它适用于共享访问和管理的场景,‌能够自动处理资源的共享和释放问题

总结

本文对堆和栈的区别,内存静态分配和动态分配,智能指针进行了介绍,总结一下就是堆可以对栈进行补充,动态分配内存,智能指针可以自动管理动态分配的对象,避免内存泄漏和资源未正确释放的问题。

  • 32
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值