C++内存部分知识归纳总结

一、低级内存管理

1.new / delete 操作符参考

new / delete 是C++的关键字,这个关键字是给编译器识别和使用了。当调用一个new只是其实是编译帮我们调用了new 对应的分配函数来创建内存空间,并返回内存地址给我们使用,必须手动的delete释放内存。

C++ 程序可以为这些函数提供全局和类特定的替换。我们可以重新这些函数。
如果 new 表达式以可选的 :: 运算符开头,如 ::new T 或 ::new T[n],则类特定的替换将被忽略(该函数在全局作用域中查找)。否则,如果 T 是类类型,则查找从 T 的类作用域开始。(也就是有全局版本和类中版本的)

(1).new / delete的使用

void testNew()
{
	//::(可选) new (类型 ) new - initializer (可选)	(1)
	//::(可选) new 类型 new - initializer (可选)	(2)
	//::(可选) new (placement - args ) (类型 ) new - initializer (可选)	(3)
	//::(可选) new (placement - args ) 类型 new - initializer (可选)	(4)

	//::(可选) delete   表达式	(1)	
	//::(可选) delete[] 表达式	(2)	


	//::new 是使用了全局的new,不写的::的话默认也是全局的
	auto a = ::new int(3);
	auto b = ::new int(3 + 5);
	auto c = new (std::nothrow) int(3 + 5);
	auto d = new (a) int(10);

	auto q = new std::integral auto(1);


	cout << "值:" << "a:" << *a << "\tb:" << *b << "\tc:" << *c << "\td:" << *d << "\tq:" << *q << endl;
	cout << "地址:" << "a:" << (long long)a << "\tb:" << (long long)b << "\tc:" << (long long)c << "\td:" << (long long)d << "\tq:" << (long long)q << endl;

	delete a;
	delete b;
	delete q;


	//动态数组
	int cout = 2;
	int size = 3;

	const int cout2 = 2;

	//int arr[cout][cout];错误,必须是常量表达式
	int arr[2][3];
	int arr2[cout2][cout2]; //正确,必须是常量表达式
	//int arr3[cout][5];    错误,栈中的二维数组必须是常量表达式

	auto p1 = new  double[cout][5];  //除第一个维度之外的所有维度都必须指定为正的整型常量表达式(C++14 前)转换为 std::size_t 类型的常量表达式
	auto p2 = new double[cout][2][3];
	//二维数组的动态分配
	//(1)连续的二维空间数组,但是第二维度必须固定
	int(*p3)[5] = new int[cout][5];
	//(2)不连续的二维空间数组,第二维度可以不固定,数组指针
	int** p4 = new int* [cout];//行数
	for(auto i = 0; i < cout; i++)
	{
		p4[i] = new int[size];//每行数据个数
	}
	
	//释放二维数组的动态分配
	delete[] p1;
	delete[] p2;
	delete[] p3;
	for (auto i = 0; i < cout; i++)
	{
		delete[] p4[i];
	}
	delete[] p4;
}

2.分配函数,释放函数参考

(1).重载全局

就是重写几个给编译器用的分配函数,底层还是用malloc函数分配和free释放。
能够追踪到堆空间的大小分配和释放情况。

#include <cstdio>
#include <cstdlib>
#include <new>

// no inline, required by [replacement.functions]/3
void* operator new(std::size_t sz)
{
	std::printf("1) new(size_t), size = %zu\n", sz);
	if (sz == 0)
		++sz; // avoid std::malloc(0) which may return nullptr on success

	if (void* ptr = std::malloc(sz))
		return ptr;

	throw std::bad_alloc{}; // required by [new.delete.single]/3
}

// no inline, required by [replacement.functions]/3
void* operator new[](std::size_t sz)
{
	std::printf("2) new[](size_t), size = %zu\n", sz);
	if (sz == 0)
		++sz; // avoid std::malloc(0) which may return nullptr on success

	if (void* ptr = std::malloc(sz))
		return ptr;

	throw std::bad_alloc{}; // required by [new.delete.single]/3
}

void operator delete(void* ptr) noexcept
{
	std::puts("3) delete(void*)");
	std::free(ptr);
}

void operator delete(void* ptr, std::size_t size) noexcept
{
	std::printf("4) delete(void*, size_t), size = %zu\n", size);
	std::free(ptr);
}

void operator delete[](void* ptr) noexcept
{
	std::puts("5) delete[](void* ptr)");
	std::free(ptr);
}

void operator delete[](void* ptr, std::size_t size) noexcept
{
	std::printf("6) delete[](void*, size_t), size = %zu\n", size);
	std::free(ptr);
}

void* operator new(size_t sz, ::std::nothrow_t const&) noexcept
{
	std::printf("7) new(size_t), size = %zu\n", sz);
	if (sz == 0)
		++sz; // avoid std::malloc(0) which may return nullptr on success
	return std::malloc(sz);
}

(2).重载类中的

#include <cstddef>
#include <iostream>

// class-specific allocation functions
struct X
{
	static void* operator new(std::size_t count)
	{
		std::cout << "custom new for size " << count << '\n';
		return ::operator new(count);
	}

	static void* operator new[](std::size_t count)
	{
		std::cout << "custom new[] for size " << count << '\n';
		return ::operator new[](count);
	}

	// custom placement delete
	static void operator delete(void* ptr)
	{
		std::cout << "custom  delete called" << '\n';
		::operator delete(ptr);
	}

	//X() { throw std::runtime_error(""); }

	// custom placement new
	static void* operator new(std::size_t count, bool b)
	{
		std::cout << "custom placement new called by bool, b = " << b << '\n';
		return ::operator new(count);
	}

	static void* operator new(std::size_t count, int b)
	{
		std::cout << "custom placement new called by int, b = " << b << '\n';
		return ::operator new(count);
	}

	// custom placement delete
	static void operator delete(void* ptr, bool b)
	{
		std::cout << "custom placement delete called by bool, b = " << b << '\n';
		::operator delete(ptr);
	}



	int index;
	int age;
};

void testClassNewAndDelete()
{
	X* p1 = new X;
	delete p1;
	X* p2 = new X[10];
	delete[] p2;

	try
	{
		[[maybe_unused]] X* p1 = new (true) X;
	}
	catch (const std::exception&)
	{
	}
}

std::set_new_handler和std::get_new_handler 参考

如果set_new_handler,成为新的全局 new-handler 函数,并返回先前安装的 new-handler。
new-handler 函数是在每次内存分配尝试失败时,由分配函数调用的函数。其预期目的有三:

  1. 提供更多内存,
  2. 终止程序(例如通过调用 std::terminate),
  3. 抛出类型为 std::bad_alloc 或派生自 std::bad_alloc 的异常。

测试例子

#include <iostream>
#include <new>

void handler()
{
	std::cout << "Memory allocation failed, terminating\n";
	std::set_new_handler(nullptr);
}

void testNewHandler()
{
	std::new_handler h1 =  std::set_new_handler(handler);

	cout << "函数地址:" << (long long)h1 << " \t" << (long long)handler << "\t" <<(long long)get_new_handler()<< endl;

	try
	{
		while (true)
		{
			new int[1000'000'000ul]();
		}
	}
	catch (const std::bad_alloc& e)
	{
		std::cout << e.what() << '\n';
	}
}

二、智能指针

//智能指针的使用
void testSmartPointer()
{
	// unique_ptr
	//1.不支持拷贝构造和拷贝赋值,支持移动构造和移动赋值
	//2.智能指针的生命周期结束时,自动释放所管理的对象
	//3.智能指针可以自定义删除器,
	//	1)内存是由库申请的,要由库释放
	//	2)管理 C 风格分配的内存
	//	3)管理系统资源(文件句柄、文件描述符、套接字等)
	//	4)销毁对象前需要执行额外操作,比如打日志、递减引用计数、释放关联资源、通知其他模块等

	unique_ptr<CharA> up1(new CharA('a'));
	unique_ptr<CharA> up2 = make_unique<CharA>('b');
	//unique_ptr<int> up3 = up1;  
	unique_ptr<CharA> up4 = move(up1);	// up1 变为空指针,已经释放了up1的资源
	//*up1 = 100;						// 错误: up1 已经释放了资源,up1 变为空指针
	up1.release();						// up1资源已经为空,再次up1的空资源并没有影响
	unique_ptr<CharA> up5;
	up5.reset(up2.release());			// up2释放了资源,up5重新接管up2的资源
	CharA* p = up4.release();			// up4释放了资源,但要对释放的资源要手动管理
	delete p;							// 手动释放资源
	up5.reset(new CharA('c'));			//先释放up5的资源,再接管新的资源

	//删除器
	unique_ptr<CharA, CharADeleter> up6(new CharA('d'), CharADeleter());
	up6.get_deleter().close();	//获取删除器对象,并调用删除器的close方法

	// shared_ptr
	//1.支持拷贝构造和拷贝赋值,支持移动构造和移动赋值
	//2.数据访问非线程安全,引用计数线程安全
	shared_ptr<int> sp1 (new int(5));
	shared_ptr<int[]> sp2 = make_shared<int[]>(10);
	*sp1 = 100;
	sp2[0] = 100;
	shared_ptr<CharA> sp3 = make_shared<CharA>('e');
	shared_ptr<CharA> sp4 = sp3;
	cout << "引用计数:" << sp4.use_count() << endl;

	{
		shared_ptr<CharA> sp5(new CharA('f'), [](auto* p) {
			cout << "call delete lamuda" << endl;
			delete p;
		});
	}

	//weak_ptr 弱引用指针
	//解决循环引用问题

}//出了作用域,释放所有的智能指针中的资源

三、分配器和自定义分配器

template <typename _Ty>
class MyAllocator 
{
public:
	MyAllocator() {}						//“MyAllocatorB<CharA>::MyAllocatorB”: 没有合适的默认构造函数可用
	template <class Other>
	MyAllocator(const MyAllocator<Other>&) {}//“static_cast”: 无法从“MyAllocatorB<CharA>”转换为“MyAllocatorB<_Newfirst>”

	_Ty* allocate(const size_t count)		//"allocate": 不是 "MyAllocatorB<_Newfirst>" 的成员
	{
		cout << "allocate !!! :   "<<count << endl;
		return static_cast<_Ty*>(malloc(count * sizeof(_Ty)));
	}

	void deallocate(_Ty* const ptr, const size_t count)
	{
		cout << "deallocate !!!" << endl;
		free(ptr);
	}

	using value_type = _Ty;
};

//测试分配器
void testAllocator()
{
	//分配器
	{
		allocator<CharA> data_alloc;			//构建分配器
		int size = 2;
		CharA* p = data_alloc.allocate(size);	//分配器空间
		for (int i = 0;i < size;++i)
		{
			allocator_traits<decltype(data_alloc)>::construct(data_alloc, &p[i], 'a');		//构造对象

			allocator_traits<decltype(data_alloc)>::destroy(data_alloc, &p[i]);				//析构对象
		}

		data_alloc.deallocate(p, size);			//释放分配器空间
	}

	//给vector提供分配器
	vector<CharA, MyAllocator<CharA>> ve;
	ve.emplace_back('a');
	ve.emplace_back('c');
}

四、未初始化内存算法

//未初始化拷贝
void testUninitialized()
{
    //未初始化内存拷贝
    CharA data[3]{0, 1, 2};
    unsigned char buf[1024] = {0};
    std::uninitialized_copy(begin(data), end(data), (CharA*)buf);

    //未初始化内存构造
    int size = 3;
    auto data1 = static_cast<CharA*>(malloc(sizeof(CharA) * size));
    for(int i = 0; i < size; i++)
    {
        construct_at(&data1[i], static_cast<char>('a' + i));//构造
    }

    // for(int i = 0; i < size; i++)
    // {
    //     destroy_at(&data1[i]);                       //析构
    // }
    destroy(data1, data1 + size);       //析构
    free(data1);
}

五、内存池

//内存池测试
void testMemoryPool()
{
    //内存池控制
    pool_options options;
    options.largest_required_pool_block = 1024 * 1024 * 10;//10M为大数据块
    options.max_blocks_per_chunk = 1024 * 1024 * 100;
    //线程安全内存池
    synchronized_pool_resource mpool(options);
    int size = 1024 * 1024;
    std::vector<void*> datas;
    for(int i= 0;i< 1000;i++)
    {
        try{
            //从内存池申请空间
            auto data = mpool.allocate(size);
            cout <<  "+"<< flush;
            datas.push_back(data);
            this_thread::sleep_for(chrono::milliseconds(1));//sleep 10ms
        }
        catch(std::exception& e){
            cout << "bad_alloc: " << e.what() << endl;
        }

    }


    auto b1 = mpool.allocate(1024 * 1024 * 20);
    mpool.deallocate(b1, 1024 * 1024 * 20);

    for(auto d : datas)
    {
        mpool.deallocate(d,size);
        cout << "-" << flush;
        this_thread::sleep_for(chrono::milliseconds(20));
    }
    mpool.release();

    getchar();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值