什么是移动构造函数(自己deepseek一下)

关键步骤:

  1. 实现移动构造函数
    语法:test(test&& other) noexcept;

    • 从 other 中“窃取”资源(如指针、文件句柄等)。

    • 将 other 的状态置为有效但可析构的状态(例如将其指针设为 nullptr)。

  2. 实现移动赋值运算符
    语法:test& operator=(test&& other) noexcept;

    • 释放当前对象的资源。

    • 从 other 中“窃取”资源。

    • 将 other 的状态置为有效但可析构的状态。

  3. 标记为 noexcept
    移动操作通常不应抛出异常,这对标准库容器(如 std::vector)的优化很重要。

  4. ..........................................................................自己deepseek一下

看例子,有两个类:

类_Str:

/// <summary>
///  字符串类
/// </summary>
/// <typeparam name="T"></typeparam>
/// 创建时间: ????-??-??     最后一次修改时间:2022-11-13
template<typename T>
class _Str  //不要继承任何类
{
public:
	using size_type = size_t;
	using value_type = T;
protected:
	T*			_pData = null;					    //指针,指向第一个元素
	size_t		_nLength = 0;						//无素个数
	size_t      _nBuffer = 0;						//剩余缓冲区大小
	size_t      _nDefaultBuffer = 8;				//每次分配内容多分配缺省缓冲区大小
	size_t      _nAutoBufferCount = 0;				//自动设置缓冲次数的计数器

_Str的初化函数:

	/// <summary>
	/// 初始化数据,并设置缓冲区大小,如果设置bInitValue == true,
	/// 则所有缓冲都会用 tValue填充,这时长度是:nBuffer。
	/// </summary>
	/// <param name="nBuffer"></param>
	/// <param name="tValue"></param>
	/// <param name="bInitValeu"></param>
	/// 创建时间: ????-??-??      最后一次修改时间:2024-04-21
	inline void initData(const size_t& nBuffer,const T& tValue = 0, const bool bInitValue = false)
	{
#ifdef  _STR_DEBUG_
		_cout << _t("_Str<T>:\t inline void initData(const int& nBuffer) 参数:") << _geti(nBuffer) << _t("\n");
#endif  
	 
		if (bInitValue){
			_nLength = nBuffer;
			_nBuffer = 0;
			_pData = _Memory::New<T>(_nLength + 1);

			for (int i = 0; i < _nLength; ++i){
				_pData[i] = tValue;
			}
			_pData[_nLength] = 0;
		}else{
			_nLength = 0;
			_nBuffer = nBuffer;
			_pData = _Memory::New<T>(_nBuffer + 1);
			_pData[0] = 0;
		}
	}

类_StrW:

class _StrW : public _Str<wchar_t>
{
public:

看他们的两个构造函数,完全一样的代码,一个会造成内存泄露,一个不会,你能看出问题来吗?

代码1

	/// <summary>
	/// 移动构造函数,std::move 的本质	它只是将左值强制转换为右值引用
	/// (static_cast<T&&>),本身不执行任何移动操作。
	/// </summary>
	/// <param name="other"></param>
	/// 创建时间: 2025-04-21      最后一次修改时间:2025-04-21 
	inline _Str<T>(_Str<T>&& other) noexcept
	{	 
		_pData = other._pData;      				    //指针,指向第一个元素
		_nLength = other._nLength;						//无素个数
		_nBuffer = other._nBuffer;						//剩余缓冲区大小
		_nDefaultBuffer = other._nDefaultBuffer;		//每次分配内容多分配缺省缓冲区大小
		_nAutoBufferCount = other._nAutoBufferCount; 	//自动设置缓冲次数的计数器		

		other.initData(0);		
	}

代码2

	/// <summary>
	/// 移动构造函数,std::move 的本质	它只是将左值强制转换为右值引用
	/// (static_cast<T&&>),本身不执行任何移动操作。
	/// </summary>
	/// <param name="other"></param>
	/// 创建时间: 2025-04-21      最后一次修改时间:2025-04-21 
	inline _StrW( StrW&& other) noexcept
	{	 
		_pData = other._pData;      				    //指针,指向第一个元素
		_nLength = other._nLength;						//无素个数
		_nBuffer = other._nBuffer;						//剩余缓冲区大小
		_nDefaultBuffer = other._nDefaultBuffer;		//每次分配内容多分配缺省缓冲区大小
		_nAutoBufferCount = other._nAutoBufferCount; 	//自动设置缓冲次数的计数器		

		other.initData(0);		
	}

本来第二个函数应该这样写的,

	/// <summary>
	/// 移动构造函数,std::move 的本质	它只是将左值强制转换为右值引用
	/// (static_cast<T&&>),本身不执行任何移动操作。
	/// </summary>
	/// <param name="other"></param>
	/// 创建时间: 2025-04-21      最后一次修改时间:2025-04-21 
	inline _StrW(_StrW&& other)  noexcept : _Str<wchar_t>(std::move(other)) {}

但我匆匆忙忙直接把父类的代码复制过来了,后来发现有内存

泄露,对着下面几行代码检查了半天,搞得都快怀疑人生了,无

论如何也没发现问题?

        _pData = other._pData;                          //指针,指向第一个元素
        _nLength = other._nLength;                        //无素个数
        _nBuffer = other._nBuffer;                        //剩余缓冲区大小
        _nDefaultBuffer = other._nDefaultBuffer;        //每次分配内容多分配缺省缓冲区大小
        _nAutoBufferCount = other._nAutoBufferCount;     //自动设置缓冲次数的计数器        

        other.initData(0);        

后来终于发现了问题,_pData先被父类初始化了,而我没有释放内存,感谢deepseek,它用

这句话提醒了我。

 

1. 未清理当前对象的原有内存

移动构造函数在接管 other 的资源前,没有释放 this 对象可能已持有的内存。如果 this 已经指向某个内存块,直接覆盖指针会导致内存泄漏。

 

附:用自己的new 和 delete统计申请的内存和释放的内存数量

例如:

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

/// <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>
/// 创建时间:????-??-??    最后一次修改时间:2024-11-29
template<typename T> static  void Delete(T*  pMemory, int  nCount)
{ 
	_obj_used -= nCount;
	_mem_used -= sizeof(T) * nCount;
	delete[] pMemory;		
}


/// <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 (nCount > _memory_allow_max){ 

		throw "超出最大充许申请的对象数"; 
	}

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

	if (bZero) {
		return  new T[nCount]();	
	}else {
		return new T[nCount];
	}		
}

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐