34、C++ Primer 4th笔记,特殊工具与技术,优化内存分配(1)

简介: 1、C++的内存分配是一种类型操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象。new表达式自动运行合适的构造函数来初始化动态分配的类类型对象。 2、在每种情况下(预先分配内存以保存用户级(user-level objects)对象或者保存类的内部数据)都需要将内存分配与对象构造分离开。

1C++的内存分配是一种类型操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象。new表达式自动运行合适的构造函数来初始化动态分配的类类型对象。

2、在每种情况下(预先分配内存以保存用户级(user-level objects)对象或者保存类的内部数据)都需要将内存分配与对象构造分离开。

3、对未构造的内存中的对象进行赋值而不是初始化,其行为是未定义的。对许多类而言,这样做引起运行时崩溃。

4C++提供下面两种方法分配和释放未构造的原始内存。

1allocator 类,它提供可感知类型(type-aware)的内存分配。这个类支持一个抽象接口,以分配内存并随后使用该内存保存对象。

2)标准库中的 operator new operator delete函数,它们分配和释放需要大小的原始的、未类型化的内存。

5C++ 还提供不同的方法在原始内存中构造和撤销对象。

1allocator 类定义了名为 construct destroy 的成员:construct 成员在未构造内存中初始化对象,destroy 成员在对象上运行适当的析构函数。

2)定位 new 表达式(placement new expression)接受指向未构造内存的指针,并在该空间中初始化一个对象或一个数组。

3)可以直接调用对象的析构函数来撤销对象。运行析构函数并不释放对象所在的内存。

4)算法 uninitialized_fill uninitialized_copy fill copy算法一样执行,除了它们在目的地构造对象而不是给对象赋值之外。

现代 C++ 程序一般应该使用 allocator 类来分配内存,它更安全更灵活。但是,在构造对象的时候,用 new 表达式比allocator::construct 成员更灵活。有几种情况下必须使用new

6allocator

allocator类是一个模板,它提供类型化的内存分配以及对象构造与撤销。

allocator<T> a;

定义名为aallocator对象,可以分配内存或构造T类型的对象

a.allocate(n)

分配原始的未构造内存以保存 T 类型的 n 个对象

a.deallocate(p, n)

Deallocates memory that held n objects of type T starting at address contained

in the T* pointer named p . It is the user's responsibility to run destroy on

any objects that were constructed in this memory before calling deallocate .

a.construct(p, t)

T* 指针 p 所指内存中构造一个新元素。运行 T 类型的复制构造函数用 t 初始化该对象

a.destroy(p)

运行 T* 指针 p 所指对象的析构函数

uninitialized_copy(b, e, b2)

从迭代器 b e 指出的输入范围将元素复制到从迭代器b2 开始的未构造的原始内存(unconstructed, raw memory)中。该函数在目的地构造元素,而不是给它们赋值。假定由 b2 指出的目的地足以保存输入范围中元素的副本

uninitialized_fill(b, e, t)

将由迭代器 b e 指出的范围中的对象初始化为 t 的副本。假定该范围是未构造的原始内存。使用复制构造函数构造对象

uninitialized_fill_n(b, e, t, n)

将由迭代器 b e 指出的范围中至多 n 个对象初始化为t 的副本。假定范围至少为n个元素大小。使用复制构造函数构造对象

    allocator类将内存分配和对象构造分开。当allocator对象分配内存的时候,它分配适当大小并进行内存对齐(aligned)来保存给定类型对象的空间。但是,它分配的内存是未构造的,allocator的用户必须分别constructdestroy放置在该内存中的对象。

1)使用allocator管理类成员数据

示例代码

#include "iostream"
#include "string"
#include "vector"

using namespace std;

template <class T>
class MyVector
{
public:
	MyVector():elements(0), first_free(0), end(0){}
	void Push_back(const T&);
	//...
private:
	static std::allocator<T> alloc; //object to get raw memory
	void reallocate(); //get more space and copy existing elements.
	T* elements; //pointer to first element in the array.
	T* first_free; //pointer to first free element in the array
	T* end; //pointer to one past the end of the array.
    //...
}

template <class T>
void MyVector<T>::Push_back(const T& t)
{
	if (first_free == end)
		reallocate(); //get more space and copy existing elements.
	alloc.construct(first_free, t);
	++first_free;
}

template <class T>
void MyVector<T>::reallocate()
{
	//compute size of current array and allocate space for twice as many elements
	std::ptrdiff_t size = first_free - elements;
	std::ptrdiff_t newcapacity = 2 * max(size, 1);
	//allocate space to hold newcapacity number of elements of type T
	T* newelements = alloc.allocate(newcapacity);
	//construct copies of the existing elements in the new space
	uninitialized_copy(elements, first_free, newelements);
	//destory the old elements in reverse older
	for (T *p = first_free; p != elements; /*empty*/)
		alloc.destroy(--p);

	//deallocate cannot be called on a 0 pointer
	if (elements)
		//return the memory that held the elements
		alloc.deallocate(elements, end - elements);
	//make our data structure point to the new elements
	elements = newelements;
	first_free = elements + size;
	end = elements + newcapacity;
}

wps_clip_image-11685

7operator new函数和operator delete函数

// new expression

string * sp = new string("initialized");

    的时候,实际上发生三个步骤。首先,该表达式调用名为 operator new 的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象;接下来,运行该类型的一个构造函数,用指定初始化式构造对象;最后,返回指向新分配并构造的对象的指针

当使用 delete 表达式

delete sp;

    删除动态分配对象的时候,发生两个步骤。首先,对 sp 指向的对象运行适当的析构函数然后,通过调用名为 operator delete 的标准库函数释放该对象所用内存。

    我们不能重定义newdelete表达式的行为。

1operator newoperator delete接口

void *operator new(size_t); // allocate an object

void *operator new[](size_t); // allocate an array

void *operator delete(void*); // free an object

void *operator delete[](void*); // free an array

2)使用分配操作符函数

虽然 operator new operator delete 函数的设计意图是供 new 表达式使用,但它们通常是标准库中的可用函数。可以使用它们获得未构造内存,它们有点类似 allocate 类的 allocator deallocate 成员。

使用示例

	T* newelements = alloc.allocate(newcapacity);
	//上式表达与下式表达等价
	T* newelements = static_cast<T*>(operator new[](newcapacity * sizeof(T)));

	if (elements)
		//return the memory that held the elements
alloc.deallocate(elements, end - elements);
	//	//上式表达与下式表达等价
	operator delete[](elements);

注意:使用allocatoroperator new有一个重要的区别就是:operator newvoid*指针而不是类型化的指针上进行操作。一般而言,使用allocator比直接使用operator newoperator delete函数更为类型安全。

allocate成员分配类型化的内存,所以使用它的程序可以不必计算以字节为单位的所需内存量,它们也可以避免对 operator new 的返回值进行强制类型转换。类似地,deallocate释放特定类型的内存,也不必转换为void*

8、定位new表达式

    标准库函数 operator new operator delete allocator allocate deallocate 成员的低级版本,它们都分配但不初始化内存。

    There are also lower-level alternatives to the allocator members construct and destroy . Thesemembers initialize and destroy

objects in space allocated by an allocator object.

类似于construct成员,第三种new表达式,称为定位new(placement new)。定位new表达式在已分配的原始内存中初始化一个对象,它与new的其他版本的不同在于:它不分配内存,相反,它接受指向已分配但未构造内存的指针,并在该内存中初始化一个对象。实际上,定位new表达式使我们能够在特定的、预分配的内存地址构造一个对象。

new (place_address) type

new (place_address) type (initializer-list)

    其中 place_address 必须是一个指针,而 initializer-list 提供了(可能为空的)初始化列表,以便在构造新分配的对象时使用。

使用示例

	alloc.construct(first_free, t);
	//上式等价于下式
	//copy t into element addressed by first_free
	new(first_free) T(t);

定位new表达式比allocator类的construct成员更灵活。定位new表达式初始化一个对象的时候,它可以使用任何构造函数,并直接建立对象。construct函数总是使用复制构造函数。

示例代码

allocator<string> alloc;
string *sp = alloc.allocate(2); //allocate space to hold 2 strings
//two ways to construct a string from a pair of iterators
new (sp) string(b, e); // construct directly in place
alloc.construct(sp + 1, string(b, e)); //build and copy a temporary
目录
相关文章
|
2月前
|
存储 机器学习/深度学习 PyTorch
119_LLM训练的高效内存管理与优化技术:从ZeRO到Flash Attention
大型语言模型(LLM)的训练面临着前所未有的计算和内存挑战。随着模型规模达到数百亿甚至数千亿参数,高效的内存管理成为训练成功的关键因素之一。2025年,LLM训练的内存优化技术已经取得了显著进展,从ZeRO优化器到Flash Attention等创新技术,为训练超大规模模型提供了可能。
|
9月前
|
存储 算法 数据处理
公司局域网管理中的哈希表查找优化 C++ 算法探究
在数字化办公环境中,公司局域网管理至关重要。哈希表作为一种高效的数据结构,通过哈希函数将关键值(如IP地址、账号)映射到数组索引,实现快速的插入、删除与查找操作。例如,在员工登录验证和设备信息管理中,哈希表能显著提升效率,避免传统线性查找的低效问题。本文以C++为例,展示了哈希表在局域网管理中的具体应用,包括设备MAC地址与IP分配的存储与查询,并探讨了优化哈希函数和扩容策略,确保网络管理高效准确。
|
4月前
|
机器学习/深度学习 监控 安全
解密虚拟化弹性内存:五大核心技术与实施策略
本文深入解析虚拟化环境中实现内存弹性管理的五大核心技术与实施策略。内容涵盖内存架构演进、关键技术原理、性能优化方法及典型问题解决方案,助力提升虚拟机密度与资源利用率。
217 0
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
167 2
|
KVM 虚拟化
KVM的热添加技术之内存
文章介绍了KVM虚拟化技术中如何通过命令行调整虚拟机内存配置,包括调小和调大内存的步骤,以及一些相关的注意事项。
333 4
KVM的热添加技术之内存
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
272 2
|
12月前
|
人工智能 物联网 C语言
SVDQuant:MIT 推出的扩散模型后训练的量化技术,能够将模型的权重和激活值量化至4位,减少内存占用并加速推理过程
SVDQuant是由MIT研究团队推出的扩散模型后训练量化技术,通过将模型的权重和激活值量化至4位,显著减少了内存占用并加速了推理过程。该技术引入了高精度的低秩分支来吸收量化过程中的异常值,支持多种架构,并能无缝集成低秩适配器(LoRAs),为资源受限设备上的大型扩散模型部署提供了有效的解决方案。
750 5
SVDQuant:MIT 推出的扩散模型后训练的量化技术,能够将模型的权重和激活值量化至4位,减少内存占用并加速推理过程
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
620 10
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
647 1
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化2
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
217 6