动态内存基础
栈的对象在内存中开辟的空间都比较紧密,连续创建的对象x和y,他们在栈空间是非常紧密的。函数调用,栈针就会出栈。函数结束,对象就自动销毁了。
堆是在运行期动态扩张。因为有的时候,需要多少内存,是在运行期才能确定的。所以就需要堆。比如刚开始分配了10个int的内存,使着过程中,发现不够用了,再分配20个int。这就是动态的扩展内存,动态缩小内存也同理。
对比栈的内存,栈针弹出会自动销毁。但是堆不会,需要通过delete进行释放。
如果控制堆内存呢?我们通过new和delete来构造、销毁对象。
栈内存:
int main()
{
int x;
x = 2;
cout << x << endl;
}
堆内存:
int main()
{
int* y = new int(2);
cout << *y << endl;
delete y;
}
int* fun()
{
int* res = new int(2);
return res;
}
int main()
{
int* y = fun();
cout << *y << endl;
delete y;
}
int* fun()
{
int res = 2;
return &res;
}
int main()
{
int* y = fun();
cout << *y << endl;
delete y;
}
对象的构造:1分配内存;2在内存上构造对象; 对象的销毁:1对象销毁;2把之前分配的内存归还给系统;
int main()
{
int* y = new int[5]{1, 11, 22, 33, 44};
cout << y[1] << endl;
delete[] y;
}
内存片段:有时候我们分配了一大块连续的内存,但是释放的时候,可能只是释放了其中一部分零散的内存,所以不连续。
通过nothrow new来判断分配内存是否成功。如果失败了,系统不抛出异常,而是返回一个nullptr的指针。
#include <iostream>
#include <new>
using namespace std;
int main()
{
int* y = new(std::nothrow) int[5]{1, 11, 22, 33, 44};
if (y == nullptr)
{
}
else
{
cout << y[1] << endl;
delete[] y;
}
}
palecement new:不需要分配内存了,只需要在已分配的内存上构造对象。vector就是用这个原理。vector动态增长。
vector增加一个元素的原理不是:若来了一个新的元素,先开辟一块新的内存,把原有的值拷贝过来,再把新的元素放进来。vector一般不这么做,而是多分配一些。这样不用一直拷贝。
int main()
{
int* y = new auto(3);
}
new与对象对齐 delete
int main()
{
int* ptr = new int;
delete ptr;
int* ptr2 = new int[5];
delete[] ptr2;
}
placement delete:只关注内存上构造的对象销毁掉,但是不会把内存归还给系统。还是vector的删除一个元素的原理,只会把最后一个元素销毁掉,但是对象对应的内存并不归还给系统。
对于内建的类型,不需要考虑placement delete。只有我们定义了类的析构函数。析构函数会刷新缓存,关掉文件。
int main()
{
int* ptr = new int(3);
cout << ptr << endl;
delete ptr;
ptr = nullptr;
cout << ptr << endl;
delete ptr;
}
智能指针
new和delete是关系很紧密的两条语句,但是什么时候调用delete我们说不清,容易产生不销毁或者多销毁,而且还有内存所有权的问题。所以引入了智能指针的概念。
Shared_ptr——基于引用计数的共享内存解决方案
int main()
{
std::shared_ptr<int> x(new int(3));
cout << *x << endl;
}
share_ptr是一个智能指针,它的机制是引用计数,通过引用计数来维护内存什么时候进行销毁。我们不用担心内存泄漏,整个x在main结束,x的生命周期结束,x是一个share_ptr包含一个析构函数,x结束,析构函数销毁new int构造的内存。
#include <iostream>
#include <new>
#include <memory>
using namespace std;
int main()
{
std::shared_ptr<int> x(new int(3));
std::shared_ptr<int> y = x;
}
get()
#include <iostream>
#include <new>
#include <memory>
using namespace std;
std::shared_ptr<int> fun()
{
std::shared_ptr<int> res(new int(3));
return res;
}
int main()
{
auto y = fun();
cout << *y << endl;
cout << *(y.get()) << endl;
}
#include <iostream>
#include <new>
#include <memory>
using namespace std;
std::shared_ptr<int> fun()
{
std::shared_ptr<int> res(new int(3));
return res;
}
void fun2(int* x)
{
}
int main()
{
auto y = fun();
cout << *y << endl;
cout << *(y.get()) << endl;
fun2(y.get());
}
reset()
#include <iostream>
#include <new>
#include <memory>
using namespace std;
std::shared_ptr<int> fun()
{
std::shared_ptr<int> res(new int(3));
return res;
}
int main()
{
auto y = fun();
y.reset(new int(100));
cout << *y << endl;
}
指定内存回收逻辑:把内存放在一个池子里,一般从内存池里拿,不用的的时候放回池子里,不涉及new和delete操作,可以提升速度。 make_shared
#include <iostream>
#include <new>
#include <memory>
using namespace std;
int main()
{
std::shared_ptr<int> ptr(new int(3));
std::shared_ptr<int> ptr2 = std::make_shared<int>(3);
auto ptr2 = std::make_shared<int>(3);
}
支持数组
int main()
{
std::shared_ptr<int[]> ptr(new int[5]);
}
c++20可以用make_shared 注意: shared_ptr 管理的对象不要调用 delete 销毁
unique_ptr——独占内存的解决方案
一般意义指针就是共享的行为,但是不排除独占的时候。就要用unique_ptr
#include <iostream>
#include <new>
#include <memory>
using namespace std;
int main()
{
std::unique_ptr<int> x(new int(3));
}
int main()
{
std::unique_ptr<int> x(new int(3));
cout << x.get() << endl;
std::unique_ptr<int> y = std::move(x);
cout << x.get() << endl;
cout << y.get() << endl;
}
weak_ptr——防止循环引用而引入的智能指针
shared_ptr通过引用计数来销毁。但是17-20行 所以要解决循环引用,使用weak_ptr来解决。 weak_ptr可以和shared_ptr共享一块内存,但是weak_ptr并不会增加引用计数。这样就解决了循环引用的问题。
动态内存的相关问题
sizeof不会返回动态内存的大小:
#include <iostream>
#include <new>
#include <memory>
using namespace std;
int main()
{
int* ptr = new int(3);
cout << sizeof(ptr) << endl;
int* ptr2 = new int[3];
cout << sizeof(ptr2) << endl;
}
使用分配器( allocator )来分配内存
对象的构造分2步:1分配内存;2构造对象 allocator成员函数: allocate分配内存
int main()
{
std::allocator<int> al;
int* ptr = al.allocate(3);
}
deallocate回收内存
int main()
{
std::allocator<int> al;
int* ptr = al.allocate(3);
al.deallocate(ptr, 3);
}
使用 malloc / free 来管理内存
malloc / free另外一对内存管理的方法。对比new和delete。既然有了new和delete,为什么还要这个呢?malloc / free 继承自c语言。new和delete都包含2个操作,分配和对象。但是malloc和free只会分配内存,不会在内存上构造对象,因为c语言内部,不是很强调对象的构造。c++引入类之后,才强调对象的概念。 但是mallo不保证内存的对其,所以引入了aligned_alloc来分配内存对齐
使用 aligned_alloc 来分配对齐内存
如果有可能还是使用allocator。
动态内存与异常安全
#include <iostream>
#include <new>
#include <memory>
using namespace std;
int main()
{
int* ptr = new int(3);
delete ptr;
}
如果内存没有及时释放,内存在堆上不断的激增,最后导致系统崩溃。
#include <iostream>
#include <new>
#include <memory>
using namespace std;
void fun()
{
std::shared_ptr<int> ptr(new int(3));
}
int main()
{
}
C++ 对于垃圾回收的支持
垃圾回收:动态指定了内存,现在不用了,需要回收掉。python,java都支持垃圾回收。系统会判断内存没有使用,然后自动回收。c++对于垃圾回收可以说是欲拒还迎的状态。
|