c++学习——动态内存管理

动态内存管理

一、动态内存基础

1.1 栈内存 堆内存

  • 栈内存的特点:更好的局部性,对象自动销毁 (stack)
  • 堆内存的特点:运行期动态扩展,需要显式释放 (heap)
    • 在 C++ 中通常使用 new 与 delete 来构造、销毁对象;可以动态扩展,分配在堆内存
    • 即使在函数中 new 内存,其他地方仍然可以读取

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yG9vs5rF-1639836597353)(458d30508a72fa43e4458b95cfb13feb.png)]

#include <iostream>

int main()
{
	int* t = new int(2);
	std::cout << *t << std::endl;
    delete y;
}

1.2 对象的构造

  • 1、分配内存
  • 2、在所分配的内存上构造对象
  • 对象的销毁与之类似

1.3 new 的几种常见形式

1.3.1 构造单一对象 / 对象数组
#include <iostream>
int main()
{
	int* y = new int[5]{ 1,2,3,4,5 };//开辟4*5 字节的空间 返回首地址 
	std::cout << y[2];
	delete[] y; // 不能直接delete释放
}
1.3.2 nothrow new

对于两个占用内存中的内存片段或内存耗尽后,不足以分配内存,这时候new会抛出异常
这时候分配失败不会抛出异常,会指向nullptr

#include <iostream>
#include<new>
int main()
{
	int* y = new (std::nothrow) int[5]{ 1,2,3,4,5 };//开辟4*5 字节的空间 返回首地址 
    if(y==nullptr)
    {
        //...
    }
	std::cout << y[2];
	delete[] y;
}
1.3.3 placement new

只分配了内存,没构造对象,需要对其重新解释:

#include <iostream>
#include<new>
int main()
{
	char ch[sizeof(int)];//开辟了char[4] 和 int 一样大
	int * y = new (ch) int(4); //重新构造成int
	std::cout << *y;//但是要注意,像这样使用栈内存可能会由于函数运行结束等被释放
}
1.3.4 new auto
#include <iostream>
#include<new>
int main()
{
	int * y = new auto (4);
    //int * y = new auto ; 不能这样写
}

1.4 new 与对象对齐

#include <iostream>
#include<new>

struct alignas(1024) Str{}; //规定对齐1024字节

int main()
{
	Str* ptr = new Str(); //new能自动识别对齐
	std::cout << ptr << std::endl;
}

1.5 delete常见用法

  • 销毁单一对象 / 对象数组
    delete or delete[]
  • placement delete 把对象销毁,但是空间保留,比如vector
    析构函数中可能用到::TODO::

1.6 使用 new 与 delete 的注意事项

  • 根据分配的是单一对象还是数组,采用相应的方式销毁 [ ]
  • delete nullptr 不会执行操作
#include <iostream>
 
int main()
{
	int * x = nullptr;
    if(...)
    {
        //...
    }
	delete x;
}
  • 不能 delete 一个非 new 返回的内存
    这里只是销毁了数组,但是 y 任然存在,里面存放的地址不变
#include <iostream>
int main()
{
   int* y = new int[5]{ 1,2,3,4,5 };//开辟4*5 字节的空间 返回首地址 
   std::cout << y[2];
   delete[] y; // 不能直接delete释放
}
  • 同一块内存不能 delete 多次
    防止报错 就带 delete 后把指针指向 nullptr

  • 调整系统自身的 new / delete 行为

    • 不要轻易使用

二、 智能指针

问题:

使用 new 与 delete 的问题:::内存所有权不清晰::,容易产生不销毁,多销毁的情况

#include <iostream>
int* fun()
{
	int* res = new int[1];
	return res;
}
int main()
{
	int * x = fun();
}

2.1 C++ 的解决方案:智能指针

  • auto_ptr ( 问题多,C++17 删除)
  • shared_ptr / uniuqe_ptr / weak_ptr

2.2 shared_ptr——基于引用计数的共享内存解决方案

2.2.1基本用法
#include <iostream>
#include<memory>
 
int main()
{
	int* y(new int(3));
	//shared_ptr和普通指针用法一样,但不会内存泄漏
	std::shared_ptr<int> x(new int(3)); //引用计数 = 1
	//shared_ptr 维护了引用计数
	std::shared_ptr<int> z = x; //引用计数 = 2
	//程序结束后后 先销毁 z 后销毁 x(先构造 后销毁)
	//直到引用计数为0才会销毁
	std::cout << x.use_count();
	std::cout << z.use_count();
}
2.2.2 reset / get 方法
#include <iostream>
#include<memory>
 

std::shared_ptr<int> fun()
{
	std::shared_ptr<int> res(new int(100));
	return res;
}

int main()
{
	auto y = fun();
	std::cout << *(y.get()) << std::endl;//y.get()返回 int*,比如用在其他函数需要接受指针而不是智能指针
    std::cout << *y << std::endl;//和上一句一样
}
#include <iostream>
#include<memory>
 

std::shared_ptr<int> fun()
{
	std::shared_ptr<int> res(new int(100));
	return res;
}

int main()
{
	auto y = fun();
	y.reset(new int(3)); //引用计数为1的话 减1 释放原始资源 绑定新的地址,加1
    //如果引用计数>1的话,绑定新的地址,不释放源内存
	std::cout << *(y.get()) << std::endl;//y.get()返回 int*
    
    y.reset()//使y指向nullptr
}
2.2.3 指定内存回收逻辑
#include <iostream>
#include<memory>
void fun(int* ptr)
{
	delete ptr;
}

int main()
{
	std::shared_ptr<int> x(new int(3), fun);
}

::此程序无法运行:: shared_ptr对象销毁时会调用delete,res存在栈上,无法用delete销毁

#include <iostream>
#include<memory>

std::shared_ptr<int> fun()
{
	static int res = 3;
	return std::shared_ptr<int>(&res);
}

int main()
{
	auto y = fun();
}
解决方法

用dummy替换delete

#include <iostream>
#include<memory>
 
void dummy(int*){}
std::shared_ptr<int> fun()
{
	static int res = 3;
	return std::shared_ptr<int>(&res,dummy);
}

int main()
{
	auto y = fun();
}

这样也是个回收逻辑,当不调用的时候把数据放在内存池,但不销毁,调用的时候取出,这样就可以提升速度

2.2.4 std::make_shared
#include <iostream>
#include<memory>

int main()
{
	std::shared_ptr<int> ptr(new int (2));
	std::shared_ptr<int> ptr = std::make_shared<int>(2);
	auto ptr = std::make_shared<int>(2);
    //三种方式一样 提升构造性能
}
2.2.5 支持数组( C++17 支持 shared_ptr<T[ ]> ; C++20 支持 make_shared 分配数组)
auto ptr = std::make_shared<int[]>(4);
std::shared_ptr<int> ptr(new int[4]);
2.2.6 注意: shared_ptr 管理的对象不要调用 delete

2.3 unique_ptr——独占内存的解决方案

2.3.4 基本用法
#include <iostream>
#include<memory>

int main()
{
	//x独占这段资源
	std::unique_ptr<int> x(new  int(4));
	//不能写 std::unique_ptr<int> y = x;无法复制。
	std::unique_ptr<int> y = std::move(x);//构造将亡值,地址没变 
}
2.3.5 unique_ptr 不支持复制,但可以移动

参考上面的

#include <iostream>
#include<memory>
std::unique_ptr<int> fun()
{
    //auto res = std::make_unique<int>(3);
	std::unique_ptr<int> res(new int(3));
	return res;
}

int main()
{
	std::unique_ptr<int> x = fun();//这里就不是拷贝了 而是move
}
2.3.6 为 unique_ptr 指定内存回收逻辑

::与share_ptr不同::

2.4 weak_ptr——防止循环引用而引入的智能指针

可以看到析构函数不正常

#include <iostream>
#include<memory>
struct Str
{
	std::shared_ptr<Str> m_nei;
	~Str()
	{
		std::cout << "~Str--\n";
	}
};

int main()
{
	std::shared_ptr<Str> x(new Str{});
	std::shared_ptr<Str> y(new Str{});
	x->m_nei = y;
	y->m_nei = x;
	std::cout<<y.use_count();
}

修改:weak_ptr 可以和share_ptr共享,::但是不会增加引用计数"::

#include <iostream>
#include<memory>
struct Str
{
	std::weak_ptr<Str> m_nei;
	~Str()
	{
		std::cout << "~Str--\n";
	}
};

int main()
{
	std::shared_ptr<Str> x(new Str{});
	std::shared_ptr<Str> y(new Str{});
	x->m_nei = y;
	y->m_nei = x;
	std::cout<<y.use_count();
}
2.4.1 基于 shared_ptr 构造
2.4.2 lock 方法 判断有效性

打印出cannot 因为 y销毁 lock返回nullptr

#include <iostream>
#include<memory>
struct Str
{
	std::weak_ptr<Str> m_nei;
	~Str()
	{
		std::cout << "~Str--\n";
	}
};

int main()
{
	std::shared_ptr<Str> x(new Str{});
	{
		std::shared_ptr<Str> y(new Str{});
		x->m_nei = y;
	}

	if (auto ptr = x->m_nei.lock()) //返回;share_ptr
		std::cout << "can\n";
	else std::cout << "cannot \n";
}

三、动态内存

3.1 sizeof 不会返回动态分配的内存大小

只返会指针所占空间(64位机 -> 8)

#include <iostream>
#include<memory>

int main()
{
	int* ptr = new int[3];
	std::cout << sizeof(ptr);
}

3.2 使用分配器( allocator )来分配内存

#include <iostream>
#include<memory>

int main()
{
	std::allocator<int> al;
	int* ptr = al.allocate(3);//分配内存,但不初始化,比如给类分配,但不构造
}

3.3 使用 malloc / free 来管理内存

继承于c语言 只会分配内存,不会构造 (传入字节数)

3.4 使用 aligned_alloc 来分配对齐内存

3.5 动态内存与异常安全

3.6 C++ 对于垃圾回收的支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值