C++法则24:在标准 C++ 中,没有任何可移植的方式判断指针 T* pt 指向的内存位置是否已经 构造了对象,程序员必须手动跟踪哪些元素已构造。

假设变量ps的类型 std::string*,能否判断ps0是否已经构造,答案是:不能。

例子1:

cpp 复制代码
 
int  main()
{	
	//分配3个std::string

	std::string* ps = static_cast<std::string*>(::operator new(3 * sizeof(std::string)));

	//构造第1个std::string
	new (ps + 0) std::string("abc1");

	//构造第2个std::string
	new (ps + 1) std::string("abc2");

	//现在ps[0] 与 ps[1] 可以用。

	ps[0] = "for";
	ps[1] = "int";

	//ps[2] = "double";  //这里会出错,因为ps[2]未被构造

	std::cout << ps[0] << "\n";
	std::cout << ps[1] << "\n";


	ps[0].~basic_string();
	ps[1].~basic_string();
	//ps[1].~basic_string(); 未被构造,也不能析构
	::operator delete(ps);
}

输出:

例子2:

cpp 复制代码
int  main()
{	
	//分配3个std::string
	 
	std::string* ps = new std::string[3];

 

	ps[0] = "for";  //不能构造,因为new内部已调用构造函数
	ps[1] = "int";   //不能构造,因为new内部已调用构造函数
	ps[2] = "double";  //不能构造,因为new内部已调用构造函数
 

	std::cout << ps[0] << "\n";
	std::cout << ps[1] << "\n";
	std::cout << ps[2] << "\n";

	delete[] ps;	 
 
}

输出:

下面语句,两次new会内存丢失吗?

cpp 复制代码
string* pNew = static_cast<string*>(::operator new( 1 *sizeof(string)));

new (pNew+0) string(); //第一次

new new (pNew+0) string("abc"); //第二次new

答案是:会导致内存泄漏(严格说是资源泄漏),而且行为未定义

具体原因:

  1. 第一次 placement new 已经构造了 string 对象,分配了其内部资源(可能包括堆内存)。

  2. 第二次 placement new 在同一内存地址上再次构造新对象,会覆盖原有对象的内存,但不会调用原对象的析构函数 。因此第一次构造的 string 对象内部的资源(如动态分配的字符数组)永远不会被释放,造成泄漏。

  3. 此外,在同一地址上连续 placement new 而不先析构原对象,是未定义行为。标准规定对象的生存期结束再构造新对象时,需要先显式调用析构函数。

正确做法:如果需要替换对象,应该先析构原对象,再 placement new:

cpp 复制代码
pNew->~string();
new (pNew) string("abc");

下面是部份参考代码(完善中,只能参考):

cpp 复制代码
	/*
	功能:在已有内存上构造单个对象。

	注意:在标准 C++ 中,没有任何可移植的方式判断指针 T* pt 指向的内存位置是否已经
	构造了对象。程序员必须手动跟踪哪些元素已构造。	

	正确叫法:placement new、定位构造。

	原理:编译器在给定地址上直接调用构造函数,不涉及内存分配或格式识别。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T, typename... Args>
	T* construct_at(T* p, Args&&... args) {
		/*
		在标准 C++ 中,没有任何可移植的方式判断指针 T* pt 指向的内存位置是否已经构造了对象。

		原因
			(1)	没有"已构造标志":对象构造只是调用构造函数来初始化内存,但内存中并没有一个
				内置的标记来指示该位置是否包含一个活动对象。

			(2)	读取未构造对象是未定义行为:试图通过检查内存内容(例如读取 pt[0] 的字节)来判
				断,本身就会触发未定义行为------因为访问尚未开始生命期的对象是标准禁止的。

			(3)	平凡类型无法区分:对于 int 等平凡类型,构造与否就是内存中是否有有效值,但
				一个未初始化的 int 可能恰好包含一个"看起来有效"的数值,无法区分。

			(4)	实现可能有残留数据:即使内存尚未用于构造对象,其中可能残留之前的字节(例如
				从 ::operator new 获得的内存未清零),但这些字节不代表一个活动对象。		
		*/
		::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
		++_construct_count;   // 记录构造次数
		return p;
	}


	/*
	拷贝构造, 返回最后一个构造对象的下一个位置
	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T, typename IterClass>
	T* construct_at_list(T* p, const IterClass& itbegin, const IterClass& itEnd) {
		T* current = p;

		auto it = itbegin;

		size_t constructed = 0;
		try {
			for (; it != itEnd; ++it, ++current, ++constructed) {
				new (current) T(*it);				// 拷贝构造
				++_construct_count;                 // 记录构造次数(全局或类静态)
			}
			return current;                         // 返回最后一个构造对象的下一个位置
		}
		catch (...) {
			// 异常回滚:析构已构造的对象
			for (size_t i = 0; i < constructed; ++i) {
				(p + i)->~T();
			}
			throw;
		}
	}


	/*
	记录析构次数
	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T>
	void destroy_at(T* p, const size_t& nCount = 1) {
		if (p) {
			for (size_t n = 0; n < nCount; ++n) {
				p->~T();
			}
			_destroy_count += nCount;  // 记录析构次数		
		}
	}





 
	/*
	分配原始内存,不调用构造函数。
	参数:
		nCount						要分配的对象个数
		pCopy						要拷贝的对象指针
		nCopyCount					要拷贝的对象个数
		nDefaultConstructionCount	要初始化对象的个数(如果有剩余)

	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class T>
	T* allocate(
		const size_t& nCount,
		const T* pCopy = null,
		const size_t& nCopyCount = 0) {

		if (nCount == 0 || nCopyCount  > nCount) {
			throw "参数错误!";
		}

		if (sizeof(T) * nCount > _memory_allow_max) {
			throw "超出最大允许申请的内存";
		}
	

		_obj_used += nCount;
		_mem_used = _mem_used + sizeof(T) * nCount;



		T* pNew = static_cast<T*>(::operator new(nCount * sizeof(T)));

		/*
		//复制数据
		if (pCopy != null && nCopyCount != 0) {
			for (size_t n = 0; n < nCopyCount; ++n) {
				new (pNew + n) T(pCopy[n]);
			}
		}

		//初始化未初始化的数据
		if (nDefaultConstructionCount > 0) {
			for (size_t n = nCopyCount; n < nCopyCount + nDefaultConstructionCount; ++n) {
				new (pNew + n) T();			
			}
		}
		*/

		construct_at_list(pNew, pCopy, pCopy + nCopyCount);
	 
		return pNew;

	}


	/*
	分配nCount元素,并分配默认值

	创建时间:2026-06-14    最后一次修改时间:2026-06-14
	*/
	template<class T>
	T* allocate(const size_t& nCount, const T& defaultValue) {
		T* ptNew = allocate<T>(nCount);
		for (size_t n = 0; n < nCount; ++n) {
			new (ptNew + n) T(defaultValue);
		}

		return ptNew;
	}

	/*
	分配原始内存,不调用构造函数。
	参数:
		first						开始位置
		nCopyCount					要拷贝的对象个数
	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class ItClass, class value_type = ItClass::value_type>
	value_type* allocate(ItClass itBegin, ItClass itEnd) {

		size_t nCount = std::distance(itBegin, itEnd);

		value_type* ptNew = allocate<value_type>(nCount);

		//ItClass it = itBegin;
		//size_t n = 0;
		//while (it != itEnd) {
		//	new (ptNew + n) value_type(*it);
		//	n++;
		//}
		construct_at_list(ptNew, itBegin, itEnd);

		return ptNew;
	}

	/*
	分配原始内存,不调用构造函数

	参数:
		nCount			要删除内存的个数
		nDestructCount	要调用析构函数的个数(从0算起)

	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	allocate	英文:分配,分派;划拨;
	deallocate	英文:去分配,解除分配,重新分配;

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class T>
	void deallocate(T* p, const size_t& nCount, const size_t& nDestructCount = 0) noexcept {

		if (nCount == 0 || p == null) return;

		_obj_used -= nCount;
		_mem_used -= sizeof(T) * nCount;


		/*
		deallocate 通常只负责释放原始内存,不应主动调用析构函数,因为:
		调用者可能只构造了部分元素(例如 allocate 后只 placement new 了前几个对象)。
		析构未构造的对象是未定义行为(UB)。
		建议修改:将析构的职责完全交给调用者。deallocate 仅做两件事:
		更新统计计数器。
		调用 ::operator delete(p)。
		*/

		//for (size_t n = 0; n < nDestructCount; ++n)
		//	p[n].~T();
		destroy_at(p, nDestructCount);

		::operator delete(p);
	}

_memory.h

cpp 复制代码
/************************************************************
文件名							: _Memory.h

作者								: 李锋

功能								: 内存操作

创建时间							: 2016年7月6日

最后一次修改时间					:  2024年09月30日
**************************************************************/

#ifndef __MEMORY_H_
#define __MEMORY_H_
 
///////////////////////////////////////////////////////////////

#include "_macro.h"

///////////////////////////////////////////////////////////////
_LF_BEGIN_
 
///////////////////////////////////////////////////////////////
// 
//内存操作类
static class  _Memory {

public:
	/// <summary>
	/// 最大字符个数
	/// </summary>
	static const size_t _Maxchar = 100000000;

public:
	//全局内存对象计数器
	static  size_t  _obj_used;
	static  size_t  _mem_used;

	//充许最大内存数量
	static size_t _memory_allow_max;

	//记录构造函数次数
	static size_t _construct_count;   

	//记录析构次数
	static size_t _destroy_count;
public:

	static inline size_t allow_max() { return   _memory_allow_max;	}

	//内存使用量,字节
	static inline size_t mem_used() { return    _mem_used;}

	//对象使用量
	static inline size_t obj_used() { return    _obj_used; }


public:
	/// <summary>
	/// 功能:分配内存。
	/// 
	/// 条款3:尽量以 new 和 delete 取代malloc 和 free
	///		分配内存,给对象分配内存不能用C语言的malloc函数, 因为 malloc函数不会调用用对象的
	///		构造函数,free函数不会调用对象的析构函数。	  
	/// 条款5:使用相同形式的 new 和 delete
	///		游戏规则:如果你在调用 new 时使用了 [ ] ,则你必须在调用 delete 时也使用[] 。
	///		如果你在调用 new 的时候没有使用[],那么你也不应该在调用 delete 时使用[] 。
	/// </summary>
	/// <typeparam name="T">数据类型</typeparam>
	/// <param name="nCount">分配个数</param>
	/// <returns></returns>
	/// 创建时间: ????-??-??      最后一次修改时间:2024-12-08
	template<typename T> static  T  * New(const size_t&  nCount, const bool& bZero = false)
	{
		if (nCount <= 0)  return null;
		 
		if (sizeof(T) * nCount > _memory_allow_max)
			throw "超出最大允许申请的内存";
		 
		_obj_used += nCount;
		_mem_used = _mem_used + sizeof(T) * nCount;
	
		//对于类类型(如 _Object),使用 new T[nCount] 或 new T[nCount]() 都会调用默认构造函数。
		if (bZero) {
			return  new T[nCount]();	
		}else {
			return new T[nCount];
		}		
	}


	/*
	分配新内存,并拷贝数据。	
	创建时间: 2026-06-09      最后一次修改时间: 2026-06-09
	*/
	template<typename T> static  T* NewCopy(const T* pData, const size_t& nCopyCount)
	{
		T* pNew = New<T>(nCopyCount);
		for (size_t n = 0; n < nCopyCount; ++n) {pNew[n] = pData[n];}
		return pNew;
	}

	/*
	分配新内存,并拷贝数据。
	nCopyCount: 要拷贝的个数
	nBufferCount: 缓冲个数
	创建时间: 2026-06-09      最后一次修改时间: 2026-06-09
	*/
	template<typename T> static  T* NewCopy(const T* pData, const size_t& nCopyCount, 
		const size_t&nBufferCount){
		T* pNew = New<T>(nCopyCount + nBufferCount);
		for (size_t n = 0; n < nCopyCount; ++n) { pNew[n] = pData[n]; }
		return pNew;
	}


	/// <summary>
	/// 功能:释放内存,并把内存指针重置为0
	/// 
	/// 条款3:尽量以 new 和 delete 取代malloc 和 free
	///		分配内存,给对象分配内存不能用C语言的malloc函数, 因为 malloc函数不会调用用对象的
	///		构造函数,free函数不会调用对象的析构函数。	  
	/// 条款5:使用相同形式的 new 和 delete
	///		游戏规则:如果你在调用 new 时使用了 [ ] ,则你必须在调用 delete 时也使用[] 。
	///		如果你在调用 new 的时候没有使用[],那么你也不应该在调用 delete 时使用[] 。
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="pMemory"></param>
	/// <param name="nCount"></param>
	/// 创建时间:????-??-??    最后一次修改时间:2025-06-24
	template<typename T> static  void Delete(T* pMemory, const size_t& nCount = 1)
	{
		_obj_used -= nCount;
		_mem_used -= sizeof(T) * nCount;
		delete[] pMemory;
	}
	 
	/// <summary>
	/// 这段代码实现了自定义的 mynew 和 mydelete 函数,用于替代标准的 new 和 delete 操作
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="value"></param>
	/// <returns></returns>
	/// 创建时间:2025-07-31    最后一次修改时间:2025-07-31
	template<class T>
	T* mynew(const T& value) {

		size_t n = sizeof(T);

		T* p = static_cast<T*>(malloc(n));
		if (!p) {
			throw std::bad_alloc(); // 或 return nullptr;
		}

		try {
			new (p) T(value); // placement new
		}
		catch (...) {
			free(p); // 如果构造失败,释放内存
			throw;   // 重新抛出异常
		}
		++_obj_used;
		_mem_used += n;
		return p;
	}


	/// <summary>
	/// 这段代码实现了自定义的 mynew 和 mydelete 函数,用于替代标准的 new 和 delete 操作
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="p"></param>
	/// 创建时间:2025-07-31    最后一次修改时间:2025-07-31
	template<class T>
	void mydelete(T* p) noexcept {
		if (p) {
			size_t n = sizeof(T);
			p->~T();
			free(p);
			--_obj_used;
			_mem_used -= n;
		}
	}



	/*
	功能:在已有内存上构造单个对象。

	注意:在标准 C++ 中,没有任何可移植的方式判断指针 T* pt 指向的内存位置是否已经
	构造了对象。程序员必须手动跟踪哪些元素已构造。	

	正确叫法:placement new、定位构造。

	原理:编译器在给定地址上直接调用构造函数,不涉及内存分配或格式识别。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T, typename... Args>
	T* construct_at(T* p, Args&&... args) {
		/*
		在标准 C++ 中,没有任何可移植的方式判断指针 T* pt 指向的内存位置是否已经构造了对象。

		原因
			(1)	没有"已构造标志":对象构造只是调用构造函数来初始化内存,但内存中并没有一个
				内置的标记来指示该位置是否包含一个活动对象。

			(2)	读取未构造对象是未定义行为:试图通过检查内存内容(例如读取 pt[0] 的字节)来判
				断,本身就会触发未定义行为------因为访问尚未开始生命期的对象是标准禁止的。

			(3)	平凡类型无法区分:对于 int 等平凡类型,构造与否就是内存中是否有有效值,但
				一个未初始化的 int 可能恰好包含一个"看起来有效"的数值,无法区分。

			(4)	实现可能有残留数据:即使内存尚未用于构造对象,其中可能残留之前的字节(例如
				从 ::operator new 获得的内存未清零),但这些字节不代表一个活动对象。		
		*/
		::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
		++_construct_count;   // 记录构造次数
		return p;
	}


	/*
	拷贝构造, 返回最后一个构造对象的下一个位置
	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T, typename IterClass>
	T* construct_at_list(T* p, const IterClass& itbegin, const IterClass& itEnd) {
		T* current = p;

		auto it = itbegin;

		size_t constructed = 0;
		try {
			for (; it != itEnd; ++it, ++current, ++constructed) {
				new (current) T(*it);				// 拷贝构造
				++_construct_count;                 // 记录构造次数(全局或类静态)
			}
			return current;                         // 返回最后一个构造对象的下一个位置
		}
		catch (...) {
			// 异常回滚:析构已构造的对象
			for (size_t i = 0; i < constructed; ++i) {
				(p + i)->~T();
			}
			throw;
		}
	}


	/*
	记录析构次数
	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<typename T>
	void destroy_at(T* p, const size_t& nCount = 1) {
		if (p) {
			for (size_t n = 0; n < nCount; ++n) {
				p->~T();
			}
			_destroy_count += nCount;  // 记录析构次数		
		}
	}





 
	/*
	分配原始内存,不调用构造函数。
	参数:
		nCount						要分配的对象个数
		pCopy						要拷贝的对象指针
		nCopyCount					要拷贝的对象个数
		nDefaultConstructionCount	要初始化对象的个数(如果有剩余)

	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class T>
	T* allocate(
		const size_t& nCount,
		const T* pCopy = null,
		const size_t& nCopyCount = 0) {

		if (nCount == 0 || nCopyCount  > nCount) {
			throw "参数错误!";
		}

		if (sizeof(T) * nCount > _memory_allow_max) {
			throw "超出最大允许申请的内存";
		}
	

		_obj_used += nCount;
		_mem_used = _mem_used + sizeof(T) * nCount;



		T* pNew = static_cast<T*>(::operator new(nCount * sizeof(T)));

		/*
		//复制数据
		if (pCopy != null && nCopyCount != 0) {
			for (size_t n = 0; n < nCopyCount; ++n) {
				new (pNew + n) T(pCopy[n]);
			}
		}

		//初始化未初始化的数据
		if (nDefaultConstructionCount > 0) {
			for (size_t n = nCopyCount; n < nCopyCount + nDefaultConstructionCount; ++n) {
				new (pNew + n) T();			
			}
		}
		*/

		construct_at_list(pNew, pCopy, pCopy + nCopyCount);
	 
		return pNew;

	}


	/*
	分配nCount元素,并分配默认值

	创建时间:2026-06-14    最后一次修改时间:2026-06-14
	*/
	template<class T>
	T* allocate(const size_t& nCount, const T& defaultValue) {
		T* ptNew = allocate<T>(nCount);
		for (size_t n = 0; n < nCount; ++n) {
			new (ptNew + n) T(defaultValue);
		}

		return ptNew;
	}

	/*
	分配原始内存,不调用构造函数。
	参数:
		first						开始位置
		nCopyCount					要拷贝的对象个数
	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class ItClass, class value_type = ItClass::value_type>
	value_type* allocate(ItClass itBegin, ItClass itEnd) {

		size_t nCount = std::distance(itBegin, itEnd);

		value_type* ptNew = allocate<value_type>(nCount);

		//ItClass it = itBegin;
		//size_t n = 0;
		//while (it != itEnd) {
		//	new (ptNew + n) value_type(*it);
		//	n++;
		//}
		construct_at_list(ptNew, itBegin, itEnd);

		return ptNew;
	}

	/*
	分配原始内存,不调用构造函数

	参数:
		nCount			要删除内存的个数
		nDestructCount	要调用析构函数的个数(从0算起)

	new 运算符(new expression):例如 new T、new T(args)、new T[n]。它自动完成两件事:

	(1)	调用 operator new 分配原始内存。
	(2)	在分配的内存上调用构造函数(T::T(...))构造对象。
	(3)  operator new 分配的内存要用  operator delete 删除
	::operator new:是一个函数,只负责分配原始内存(类似 malloc),不调用构造函数。

	allocate	英文:分配,分派;划拨;
	deallocate	英文:去分配,解除分配,重新分配;

	创建时间:2026-06-13    最后一次修改时间:2026-06-14
	*/
	template<class T>
	void deallocate(T* p, const size_t& nCount, const size_t& nDestructCount = 0) noexcept {

		if (nCount == 0 || p == null) return;

		_obj_used -= nCount;
		_mem_used -= sizeof(T) * nCount;


		/*
		deallocate 通常只负责释放原始内存,不应主动调用析构函数,因为:
		调用者可能只构造了部分元素(例如 allocate 后只 placement new 了前几个对象)。
		析构未构造的对象是未定义行为(UB)。
		建议修改:将析构的职责完全交给调用者。deallocate 仅做两件事:
		更新统计计数器。
		调用 ::operator delete(p)。
		*/

		//for (size_t n = 0; n < nDestructCount; ++n)
		//	p[n].~T();
		destroy_at(p, nDestructCount);

		::operator delete(p);
	}

 


	/// <summary>
	/// 返回一个二维数组 nRows * nColoumns 二维数组,给它分配内存,
	/// 并把每个元素初始化
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="x"></param>
	/// <param name="numberofRows"></param>
	/// <param name="numberOfColumns"></param>
	/// <returns></returns>
	/// 创建时间: 2024-10-06     最后一修改时间:2024-10-06
	template<class T>
	static T** New2dArray(const size_t& nRows, const size_t& nColumns)
	{
		T** x;

		try {
			//创建行指针
			x = New<T*>(nRows);

			//为每一行分配空间,每行有 numberOfColumns 个元素
			for (size_t n = 0; n < nRows; n++)
				x[n] = New<T>(nColumns, true);

			return x;
		}
		catch (std::bad_alloc) { return null; }
	}

	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="x"></param>
	/// <param name="nRows"></param>
	/// <param name="nColumns"></param>
	/// 创建时间: 2024-10-06     最后一修改时间:2024-10-06
	template<class T>
	static void Delete2dArray(T** x, const size_t& nRows, const size_t& nColumns)
	{
		//为每一行分配空间,每行有 numberOfColumns 个元素
		for (size_t n = 0; n < nRows; n++)
			Delete<T>(x[n], nColumns);

		Delete<T*>(x, nRows);
	}



	/// <summary>
	/// 可以构造任意对象的函数
	/// </summary>
	/// <typeparam name="MyClass">要转换对象的类型</typeparam>
	/// <typeparam name="...MyClassConstructorParameterTypes">转换对象的构造函数参数类型</typeparam>
	/// <param name="...MyClassConstructorParameters">转换对象的构造函数参数</param>
	/// <returns>返回一个对象</returns>
	/// 创建时间: 2024-09-23      最后一次修改时间:2024-09-23
	template <class MyClass, typename... MyClassConstructorParameterTypes>
	MyClass ConverTo(MyClassConstructorParameterTypes&&... MyClassConstructorParameters)
	{
		return MyClass(
			std::forward<MyClassConstructorParameterTypes>(MyClassConstructorParameters)...);
	}



	/// <summary>
	/// 直接构建一个新的MyClass,并返回它的一个指针。(好处免去初始化)
	/// 用法例子:
	///
	/// list<int>* pl = NewEmplace<list<int>>(5,10); //申请一个列表内存指针(5个元素都是10的列表)
	/// delete[] pl; //删除内存对象,分配时用new[]分配的
	/// </summary>
	/// <typeparam name="MyClass"></typeparam>
	/// <typeparam name="...MyClassConstructorParameterTypes"></typeparam>
	/// <param name="...MyClassConstructorParameters"></param>
	/// <returns></returns>
	/// 创建时间: 2024-10-01      最后一次修改时间:2024-10-01
	template<typename MyClass, typename... MyClassConstructorParameterTypes>
	static  MyClass* NewEmplace(MyClassConstructorParameterTypes&&... MyClassConstructorParameters)
	{
		_obj_used += 1;
		_mem_used = _mem_used + sizeof(MyClass);


		/*
		//不能用 delete[] 删除
		return new MyClass(
			std::forward<MyClassConstructorParameterTypes>(MyClassConstructorParameters)...);

		*/

		/*
		list<int> il = { 1,2,3 };
		std::list<int>* p2 = new std::list<int>[1] {il};
		*/

		//delete[] pl; //删除内存对象,分配时用new[]分配的
		return new MyClass[1]
		{
			MyClass(
			std::forward<MyClassConstructorParameterTypes>(MyClassConstructorParameters)...) 
		};

	}

	/// <summary>
	/// 不改变原有数据,把内存段区间 [begin,pend) 向后移动nCount位,后面的
	/// 数据会pend后面会覆盖nCount-1个,包括pend就是nCount个,如果bFilling
	/// 为True,则用相应的pFilling所指的数据填充空出来的内存。
	/// 例:
	///		_string s = "sss123   www"; //中间3个空格
	///		MoveBack(s.begin(), s.begin() + 6, 3, true, s.end() - 3);
	///     结果:s=wwwsss123www
	/// 
	/// 注意:(1) pFilling 与 pbegin,pend,不能在同一区域的内存块中。
	///	注意:(2) 填空的字符长度要与空出来的字符长度一致。
	/// </summary>
	/// <typeparam name="T">数据类型</typeparam>
	/// <param name="pbegin">开始位置</param>
	/// <param name="pend">结束位置</param>
	/// <param name="nCount">移运个数</param>
	/// <param name="bFilling">是否填充</param>
	/// <param name="pFilling">指向填充数据的指针</param>
	/// <returns></returns>
	/// 创建时间:2024-10-03    最后一次修改时间:2024-10-03 (已测试)
	template<class T>
	static T* MoveBack(T* pbegin, T* pend, const size_t& nCount,
		const bool& bFilling = false, const T* pFilling = null){

		assert(pend >= pbegin);

		for (size_t n = 0; n < pend - pbegin; ++n) {

			*(pend + nCount - 1 - n) = *(pend - 1 - n);
		}

		if (bFilling && pFilling) { //检查bFilling不能在区间[pbegin,pend + nCount)中

			assert(pFilling < pbegin || pFilling >= pend + nCount);

			for (size_t n = 0; n < nCount; ++n) {

				*(pbegin + n) = *(pFilling + n);
			}
		}

		return pbegin;
	}


	/// <summary>
	/// 不改变原有数据,把内存段区间 [begin,pend) 向前移动nCount位,
	/// pbegin前面的会会覆盖nCount个,如果bFilling为True,则用相应
	/// 的pFilling所指的数据填充空出来的内存。
	/// 例:
	///		_string s = "   sss123www";
	///		MoveFont(s.begin(), s.begin() + 6, 3, true, s.end() - 3);
	///     结果:s=wwwsss123www
	/// 
	/// 注意:pFilling 与 pbegin,pend,不能在同一区域的内存块中。
	/// 
	/// </summary>
	/// <typeparam name="T">数据类型</typeparam>
	/// <param name="pbegin">开始位置</param>
	/// <param name="pend">结束位置</param>
	/// <param name="nCount">移运个数</param>
	/// <param name="bFilling">是否填充</param>
	/// <param name="pFilling">指向填充数据的指针</param>
	/// <returns></returns>
	/// 创建时间:2024-10-03    最后一次修改时间:2024-10-03 (已测试)
	template<class T>
	static T* MoveFront(T* pbegin, T* pend, const size_t& nCount,
		const bool& bFilling = false, const T* pFilling = null) {

		assert(pend >= pbegin);

		for (size_t n = 0; n < pend - pbegin; ++n) {

			*(pbegin - nCount + n) = *(pbegin + n);
		}

		if (bFilling && pFilling) { //检查bFilling不能在区间[pbegin,pend + nCount)中

			assert(pFilling < pbegin - nCount || pFilling >= pend);

			for (size_t n = 0; n < nCount; ++n) {

				*(pbegin - nCount + n) = *(pFilling + n);
			}
		}

		return pbegin;
	}


    template<class IterClass, class ValueType>
	static const IterClass MoveBackIter(const IterClass& itbegin, const IterClass& itend,
		const size_t& nCount,const bool& bFilling = false,
		const ValueType* pFilling = null){

		//assert(itend >= itbegin);  //可能很多迭代器不支持

		for (size_t n = 0; n < itend - itbegin; ++n) {

			*(itend + nCount - 1 - n) = *(itend - 1 - n);
		}

		if (bFilling && pFilling) { //检查bFilling不能在区间[pbegin,pend + nCount)中

			//可能很多迭代器不支持
			//assert(pFilling < itbegin || pFilling > itend + nCount);

			for (size_t n = 0; n < nCount; ++n) {

				*(itbegin + n) = *(pFilling + n);
			}
		}

		return itbegin;
	}

	/// <summary>
	/// 判断pt是否指向区块内存[pbegin,pend)。
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="pbegin"></param>
	/// <param name="pend"></param>
	/// <param name="pt"></param>
	/// <returns></returns>
	/// 创建时间:2024-10-04    最后一次修改时间:2024-10-04
	template<class T>
	static bool isPointsToTheSameMemoryArea(const T* pbegin, const T* pend, const T* pt)
	{
		return pt >= pbegin && pt < pend;
	}



	/// <summary>
	/// 设置内存的值
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="pMem"></param>
	/// <param name="nCount"></param>
	/// <param name="rDefaultValue"></param>
	/// <returns></returns>
	template<typename T> static   T  *Set(T *pMem,const size_t  nCount,  const T &rDefaultValue)
	{		 
		assert(pMem != null);		

		for (size_t n = 0; n < nCount; ++n)
			*(pMem + n) = rDefaultValue;		
		return pMem;
	}


	 
 
	/// <summary>
	/// 内存内存 
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="pDestination">指向目标内存指针</param>
	/// <param name="pSource">指向需要拷贝的内存</param>
	/// <param name="nCount">需要拷贝的内存个数</param>
	/// <returns>返回指向目标内存的指针</returns>
	/// 创建时间: ????-??-??(2020-01-01)      最后一次修改时间:2024-05-04
	template<typename T> static T *Copy(T *pDestination, const T *pSource, const size_t& nCount)
	{	
		//assert(nCount != 0 && pDestination != null && pSource != null);		
		//assert(pDestination != null && pSource != null);	//nCount 可以允许为0。

		for (size_t n = 0; n < nCount; ++n){

			pDestination[n] = pSource[n];		

		}
		return pDestination;
	}



	/// <summary>
	/// 在两种不同的数据之间拷贝
	/// </summary>
	/// <typeparam name="T1"></typeparam>
	/// <typeparam name="T2"></typeparam>
	/// <param name="pDestination"></param>
	/// <param name="pSource"></param>
	/// <param name="nCount"></param>
	/// <returns></returns>
	/// 创建时间: 2025-02-13      最后一次修改时间:2025-02-13
	template<class T1,class T2>
	static T1* Copy(T1* pDestination, const T2* pSource, const size_t& nCount) {
		for (int i = 0; i < nCount; ++i) {
			pDestination[i] = pSource[i];
		}

		return pDestination;
	}


	/// <summary>
	/// 比较丙段内存是否相等,相当于strcmp
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="pt1"></param>
	/// <param name="pt2"></param>
	/// <param name="nCount"></param>
	/// <returns></returns>
	/// 创建时间: 2025-03-13      最后一次修改时间:2025-03-13
	template<class T>
	static int Cmp(const T* pt1, const T* pt2, const size_t nCount) {
		for (size_t n = 0; n < nCount; ++n) {
			int diff = pt1[n] - pt2[n];
			if (diff != 0) return diff;
		}
		return 0;  //完全相等
	}


 } m;

 
 /// <summary>
 /// 内存管理,内存自动释放
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// 创建时间: ????-??-??      最后一次修改时间:2025-02-09
 template<typename T = char>
 class _Mem 
 {
 private:
	 T*			_pData = null;				//指针,指向第一个元素
	 size_t		_nElementCount = 0;			//无素个数
	 size_t     _nBufferCount = 0;			//剩余缓冲区无素个数
	 size_t     _nAutoBufferCount = 0;		//自动设置缓冲次数的计数器
	 bool		_isManaged = false;			//是否被托管的
 public: 
	 /// <summary>
	 /// 指向 以 T 为单位的内存指针。
	 /// </summary>
	 inline T* data() const { return _pData; }  
	  
	 inline T* data() { return _pData; }

	 /// <summary>
	 /// 获取以 T 为单位有效数据内存长度。
	 /// </summary>
	 inline size_t elementCount() const { return _nElementCount; };
	  
	 inline size_t bufferCount()const { return _nBufferCount; }

	 /// <summary>
	 /// 获取以 T 为单位总共分配的元素个数。
	 /// </summary>
	 /// <returns></returns>
	 inline size_t allElementCount()const { return _nElementCount + _nBufferCount; }

	 inline size_t memorySize()const { return allElementCount() * sizeof(T); }

 
	/// <summary>
	/// 重新分配内存
	/// </summary>
	/// <param name="nCount"></param>
	/// 创建时间: 2023-09-22      最后一次修改时间:2023-09-22
	 inline void reallocate(const size_t& nBuffer)
	 {
		 free();

		 if (_isManaged) {

			 _pData = _Memory::New<T>(nBuffer);
			 _nElementCount = 0;
			 _nBufferCount = nBuffer;
			 _nAutoBufferCount = 0;	 	
		 }
	 }


	/// <summary>
	/// 释放内存
	/// </summary>
	/// 创建时间: 2023-09-22      最后一次修改时间:2023-09-22
	 inline void free()
	 {
		 /*
		 T*			_pData;						//指针,指向第一个元素
		 size_t		_nCount;					//无素个数
		 size_t     _nBufferCount;				//剩余缓冲区无素个数
		 size_t     _nAutoBufferCount = 0;		//自动设置缓冲次数的计数器
		 bool		_isManaged = false;			//是否被托管的
		 */
		 if (!_isManaged)
		 {
			 _Memory::Delete<T>(_pData, allElementCount());
			 _pData = null;
			 _nElementCount = 0;
			 _nBufferCount = 0;
			 _nAutoBufferCount = 0;
		 }
	 }


	 /// <summary>
	 /// 构造函数,分配内存
	 /// </summary>
	 /// <param name="nAllLength"></param>
	 inline _Mem(const size_t& nBuffer = 0) 
	 {
		 if (nBuffer > 0){ setBuffer(nBuffer); }	 
	 }


	/// <summary>
	/// 
	/// </summary>
	/// <param name="pstr"></param>
	/// 创建时间: 2025-02-09      最后一次修改时间:2025-02-09
	 inline _Mem(const char* pstr) {		 
		 assert(pstr != 0);

		 _nElementCount = strlen(pstr);
		 _nBufferCount = 1;

		 _pData = _Memory::New<T>(_nElementCount + _nBufferCount, false);

		 for (int i = 0; i <= _nElementCount; ++i) {
			 _pData[i] = pstr[i];
		 }
	 }


	 /// <summary>
	 /// 拷贝构造
	 /// </summary>
	 /// 创建时间: 2023-05-10      最后一次修改时间:2023-05-10
	 inline _Mem(const _Mem<T>& m)
	 {
		 /*
		 T*			_pData;						//指针,指向第一个元素
		 size_t		_nCount;					//无素个数
		 size_t     _nBufferCount;				//剩余缓冲区无素个数
		 size_t     _nAutoBufferCount = 0;		//自动设置缓冲次数的计数器
		 bool		_isManaged = false;			//是否被托管的
		 */

		 if (m._isManaged) { throw "错误:被托管内存无法拷贝。"; }

		 if (this != &m) {

			 _nElementCount = m._nElementCount;
			 _nBufferCount = m._nBufferCount;
			 _nAutoBufferCount = m._nAutoBufferCount;

			 _pData = _Memory::New<T>(_nElementCount + _nBufferCount);

			 _Memory::Copy(_pData, m._pData, _nElementCount + _nBufferCount);

		 }

		 //_cout << _t("inline _Mem(const _Mem<T>& m)\n");
	 }
	    
	 /// <summary>
	 /// 把有效数据的后一位数据进行初始化,如果是数字,则会自动初始化0。
	 /// </summary>
	 inline void zeroEnd() {

		 if (_isManaged) { throw "错误:被托管内存数据不能改动!"; }

		 if (_nBufferCount > 0) _pData[_nElementCount] = T();
	 }
	  
	 /// <summary>
	 /// 初始化所有内存
	 /// </summary>
	 /// 创建时间: 2023-09-05      最后一次修改时间:2023-09-05
	 inline void zeroAll()
	 {
		 if (_isManaged) { throw "错误:被托管内存数据不能改动!";}
		 
		 for (size_t i = 0; i < allElementCount(); ++i)
		 {
			 *(_pData + i) = T();
		 }
	 }

	 inline void zeroBuffer() {
		 for (size_t n = _nElementCount; n < _nBufferCount + _nElementCount; ++n) {
			 _pData[n] = 0;
		 }
	 }


	 /// <summary>
	 /// 设置以 T 为单位的内存长度,并把有效数据的后一位数据进行初始化,如果是数字,则会自动初始化0。
	 /// </summary>
	 /// 创建时间: 2023-09-05      最后一次修改时间:2025-02-09
	 void setElementCount(const size_t& nNewElementCount)
	 {
		 if (_isManaged) { throw "错误:被托管内存数据不能改动!";}

		 size_t nAll = allElementCount();

		 assert(nNewElementCount <= nAll);

		 _nElementCount = nNewElementCount;
		 _nBufferCount = nAll - _nElementCount;
		  
		 zeroEnd();
	 }

 
	/// <summary>
	/// 设置新的缓冲数量,并添加一段新数据。
	/// 原则:
	///		先拷贝所有数据,再删除旧数据,因为
	///		当添加的数据是指向旧数据时,会出现问题。
	/// </summary>
	/// <param name="nNewBuffer"></param>
	/// <param name="pAdd"></param>
	/// <param name=""></param>
	/// <returns></returns>
	/// 创建时间: 2025-02-09      最后一次修改时间:2025-02-09
	size_t setBuffer(const size_t& nNewBuffer, const T* pAdd = null, 
		 const size_t& nAddCount = 0) {

		if (_isManaged) { throw "错误:被托管内存数据不能改动!";}


		//当缓冲足够保存新数据时
		if (_nBufferCount > nAddCount + nNewBuffer) {
			for (int i = 0; i < nAddCount; ++i) {
				_pData[_nElementCount + i] = *(pAdd + i);
			}

			_nElementCount += nAddCount;
			_nBufferCount -= nAddCount;

			return _nBufferCount;
		}

	
		 //旧内存长度
		 size_t nOld = _nElementCount + _nBufferCount;

		 T* pt = null;

		 if (pAdd != null && nAddCount != 0) {

			//分配新内存
			pt = _Memory::New<T>(_nElementCount + nNewBuffer + nAddCount);

			if(_pData != null){
				//拷贝旧数据
				_Memory::Copy(pt, _pData, _nElementCount);

				//拷贝添加的数据
				_Memory::Copy(pt + _nElementCount, pAdd, nAddCount);

				_nElementCount = _nElementCount + nAddCount;

			}else {
				  //拷贝添加的数据
				  _Memory::Copy(pt, pAdd, nAddCount);

				  _nElementCount = nAddCount;
			}
		 }else { //元素个数未改变
			 if (_pData != null) { 
				 //分配新内存
				 pt = _Memory::New<T>(_nElementCount + nNewBuffer);

				 //拷贝旧数据
				 _Memory::Copy(pt, _pData, _nElementCount);
			 }
			 else {
				 //分配新内存
				 pt = _Memory::New<T>(nNewBuffer);
			 }
		 }

		 //设置新的缓冲数量
		 _nBufferCount = nNewBuffer;


		 //删除旧内存
		 _Memory::Delete<T>(_pData, nOld);


		//指向新内存
		 _pData = pt;		

		 return _nBufferCount;
	 }


	 /// <summary>
	 /// 添加一串元素,要考虑pAdd与_pData指向同一位置,或同一段内存。
	 /// </summary>
	 /// <param name="tValue"></param>
	 /// 创建时间: 2025-02-09      最后一次修改时间:2025-02-09
	inline void add(const T* pAdd, const size_t& nAdd) {
		 if (_nBufferCount < nAdd) {
			 ++_nAutoBufferCount;
			 size_t n = 2 << _nAutoBufferCount;
			 setBuffer(n, pAdd, nAdd);
		 }
		 else {
			 setBuffer(0, pAdd, nAdd);
		 }
	 }


	 /// <summary>
	 /// 添加一个元素
	 /// </summary>
	 /// <param name="tValue"></param>
	 /// 创建时间: 2025-02-09      最后一次修改时间:2025-02-09
	 inline void add(const T& tValue) {	 add(&tValue, 1); }


	 /// <summary>
	 /// 当bManaged为True时,内存被托管,释放对象时
	 /// 不会自动释放内存。
	 /// </summary>
	 /// <param name="bManaged"></param>
	 /// 创建时间: 2025-02-09      最后一次修改时间:2025-02-09
	 inline void setManaged(const bool& bManaged) {	 this->_isManaged = bManaged; }


	 /// <summary>
	 /// 
	 /// </summary>
	 /// 创建时间: 2025-02-14      最后一次修改时间:2025-02-14
	 inline operator T* () { return _pData; }

	 /// <summary>
	 /// 析构函数,释放内存。
	 /// </summary>
	 ~_Mem() 
	 {
		 free();
	 }

	 /// <summary>
	 /// 放弃对象当前使用的内存,可能内存已托管给别的对象,
	 /// 重新初始化当前对象,除非内存给其它对象托管,否则不要调用这个函数。
	 /// </summary>
	 /// 创建时间:2026-05-04    最后一次修改时间:2026-05-04
	 inline void  giveUpMem()
	 {
		 this->setManaged(true);
	 }

 };


///////////////////////////////////////////////////////////////

 _LF_END_


///////////////////////////////////////////////////////////////
#endif  //_memory__H_
///////////////////////////////////////////////////////////////
相关推荐
代码不加糖1 小时前
Proxy能够监听到对象中的对象的引用吗?
开发语言·前端·javascript
charlie1145141912 小时前
现代C++指南:Lambda,让我们用另一种方式持有函数
开发语言·c++
森G2 小时前
77、线程池原理和实现------服务器源码解析----云视频服务项目
服务器·c++·qt
один but you2 小时前
const和constexpr常量表达式
java·前端·javascript
码云数智-大飞2 小时前
RAII 与智能指针深度拆解
java·前端·算法
云烟成雨TD2 小时前
Agent Scope Java 2.x 系列【19】Harness:从零搭建 MySQL 文件系统
java·人工智能·agent
qq3621967052 小时前
阿里裁员新消息(2026最新动态汇总)
java·开发语言·前端
a1117762 小时前
“黑夜流星“个人引导页 网页html
java·前端·html
砚底藏山河2 小时前
沪深A股:如何获取基金持股数据
java·python·数据分析·maven