一、低级内存管理
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 函数是在每次内存分配尝试失败时,由分配函数调用的函数。其预期目的有三:
- 提供更多内存,
- 终止程序(例如通过调用 std::terminate),
- 抛出类型为 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();
}


被折叠的 条评论
为什么被折叠?



