计算机世界里的“堆栈”你真的懂吗? - 知乎
栈内存特点: 更好的全局性,对象自动销毁
堆内存的特点: 运行期动态扩展,需要显示释放
int x; x在栈区 x=2; cout<int *fun() { int *res=new int(4); 合法 res开辟在堆区 作为函数返回值未销毁 y指向res return res; } int *fun2() { int res2=3; 不合法 res2开辟在栈区 作为函数返回值后已销毁 z指向res2及指向一个已经销毁的地址 return &res; } int main() { int *y=fun(); cout<<*y<new C++通常需要new和delete来构造和销毁对象
new:构造对象 先分配内存 再构造对象
delete:对象销毁 先销毁对象 再释放内存
new的形式:
1.构造单一的对象/对象数组
int main() { int x; int *y=new int; delete y; int *z=new int[5]; 开辟20个字节的内存空间 保存5个int的对象 返回首个int的地址 int *a=new int[3]{1,2,3}; cout<nothrow new 不抛异常的new 需要头文件#include
#includeint *y=new(std::nothrow)int[5]{}; if(y==nullptr){....} 如果y经过一系列操作后指向空了 不让编译器报异常从而停止程序 delete []y placement new 已有内存 只构造对象
char ch[sizeof(int)]; int *y=new(ch)int(4); 在ch包含的内存上,创建对象 此处是栈内存 cout<<*y<new auto
int *y=new auto(3); 可以推导出int类型 int *x=new auto ; errordeletedelete常见用法:
-销毁单一对象或者数组 delete pty ; delete[ ] ptr
-placement delete 只销毁对象 不释放内存
Notes:
--根据分配的是单一对象还是数组,采用相应的delete
--delete nullptr
int *x=nullptr;int *y=0; delete x;delete y; delete 什么也不做
--不能delete一个非new返回的内存 int x; delete x; error
--同一块内存不能delete多次
int *ptr=new int[5]; int *ptr2=ptr+1; delete []ptr2 error int *x=new int(4); delete x; cout<<*x<02智能指针 【C++】智能指针详解_Billy12138的博客-CSDN博客_智能指针参考资料:《C++ Primer中文版 第五版》 我们知道除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆。程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们。在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delet...https://blog.csdn.net/flowing_wind/article/details/81301001
使用new/delete的问题: 内存所有权不清晰,容易产生不销毁,多销毁的情况----
解决方法--智能指针
C++ 智能指针底层是采用引用计数的方式实现的。简单的理解,智能指针在申请堆内存空间的同时,会为其配备一个整形值(初始值为 1),每当有新对象使用此堆内存时,该整形值 +1;反之,每当使用此堆内存的对象被释放时,该整形值减 1。当堆空间对应的整形值为 0 时,即表明不再有对象使用它,该堆空间就会被释放掉。
int *fun() { int *res=new int(100); return res; } int main() { int *y=fun(); main 和fun谁拥有res的所有权 谁来释放堆区内存不清晰 } int *fun2() { static int res=200; 静态局部变量既不在堆区也不再栈区 return &res; } int main() { int *y=fun(); 通常程序执行完 系统自动销毁res }shared_ptr 共享指针auto_ptr C++98和C++03引入
shared_ptr unique_ptr weak_ptr C++11引入
shared_ptr-----基于引用计数的内存共享解决方案 包含头文件#include
多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)
1.基本用法
#include#include int main() { std::shared_ptr x(new int(4)); 初始化 cout< y=x; cout< 2.get()/reset()用法
std::shared_ptrfun() { std::shared_ptr res(new int (100)); return res; } void fun(int *ptr){....} int main() { auto y=fun(); cout<<*y< #include#include std::shared_ptr fun() { std::shared_ptr res(new int (23)); return res; } int main() { auto y=fun(); y.reset(new(int(3))); 把原来指向的内存释放掉 关联新的内存 y.reset((int*)nullptr);不关联任何内存 nullptr为nullptr_t 故需要转化为int* y.reset(); } 3.指定回收逻辑
void fun(int *ptr) { cout<<"fun in called"<x(new int(3),fun); x指向的内存引用计数为0时,会调用fun作为deleter 而不是默认的deleter } 4.std::make_shared (引入优化+构造对象)
std::shared_ptrptr(new int(3)); std::shared_ptr ptr2=std::make_shared (3); auto ptr3=std::make_shared (3); make_shared引入了优化 将关联的内存与维护引用计数的内存放的尽可能的近 使程序读取一次缓存即可 优化了程序的性能. 5.shared_ptr
C++17引入 make_shared分配数组 c++20引入 C++17之前 数组指针需要指定回收逻辑,C++17引入了对数组的支持
std::shared_ptrptr(new int [5]); auto ptr2=std::make_shared (5); 6.shared_ptr<>管理的对象不要调用delete销毁
智能指针陷阱:
(1)不使用相同的内置指针值初始化(或reset)多个智能指针。
(2)不delete get()返回的指针
(3)不使用get()初始化或reset另一个智能指针
(4)如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效
(5)如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器std::shared_ptrunique_ptr 独占内存x(new int(3)); delete x.get(); 将x指向的内存销毁,当程序执行完销毁x时,x此时为悬挂指针,调用析构函数,再次销毁指向的内存,故重复释放 std::shared_ptr y(x); x和y共享一套引用计数和内存 std::shared_ptr y(x.get()); error 重复释放 x y只共享指向的内存 int *ptr=new int(3); std::shared_ptr i(ptr); std::shared_ptr j(ptr);error重复释放 某个时刻只能有一个unique_ptr指向一个给定对象,由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。
1.基本用法
unique_ptr不支持复制 但是可以移动 也可为unique_ptr指定回收逻辑
std::unique_ptrx(new int(3)); std::unique_ptr y=x;error unique_ptr不支持复制 但是可以移动 std::unique_ptr y=std::move(x); 将亡值x 将资源全部给y std::cout< fun() { auto res=std::make_unique (4); return res; } int main() { std::unique_ptr x=fun(); res将所有权转让给x; } void fun(int *ptr) { std::cout<<"fun is called"<weak_ptr --防止循环引用引入的智能指针x(new int(3),fun); std::unique_ptr y(new int(3),fun); 需要decltype } weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。
可以和shared_ptr共享内存,但是不增加引用计数
struct Str { std::weak_ptr03动态内存相关问题m_nei; ~Str() { cout<<"~Str()is called"< x(new Str{}); std::shared_ptr y(new Str{}); x->m_nei=y; y->m_nei=x; 循环引用 若m_nei是std::shared_ptr则不能释放x y } 1.sizeof不会返回动态分配的内存大小
int *ptr=new int (3); int *ptr=new int [3]; cout<x; cout< 2.使用分配器allocator来分配内存
std::allocatoral; int *ptr=al.allocator(5); 只分配包含5个int的内存 不构造对象 包含了对齐内存的操作 al.deallocator(ptr,3) 释放内存 3.使用malloc/free来管理内存 C语言
//void * malloc(size_t size) int *p1=malloc(4*sizeof(int)); int *p2=malloc(sizeof(int [4])); int *p3=malloc(4*sizeof(*p3));; ....... free (p1); free (p2); free (p3); malloc只分配字节 只关注分配内存的大小 不构造对象 内存也不一定对齐经典面试题: new 和malloc 的区别?
C++ new和malloc的区别_Linux猿的博客-CSDN博客_c++malloc
经典面试题之new和malloc的区别_Datou_Nie的博客-CSDN博客_new和malloc的区别
1.使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸,不能对齐内存.
2.new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现.)
-malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作
3.new做两件事:分配内存和调用类的构造函数,delete是:调用类的析构函数和释放内存。而malloc和free只是分配和释放内存。
-new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中
4.使用aligned_alloc来分配对齐内存 256/4=64个int
5.动态内存与异常安全(产生异常,也会释放内存)
void fun() { int *ptr=new int(3); ... 可能有异常导致内存不能释放 使程序终止 delete ptr; } void fun() { std::shared_ptr(new int(3)); } 使用智能指针 保证异常下 也能正常释放内存 6.C++对垃圾回收的支持
Garbage collector support
-开辟单独线程(不断检测堆区的内存)消耗资源
-不能对对象的生命周期精确的控制
持续更新............................



